Flexでホワイトボードアプリを作ってみる [1]

Flexを使って、複数人がインターネット上で共有できるホワイトボードアプリを作ってみます。

まず、今回は簡単なActionScriptプロジェクトで単にフリーハンドの線を複数人で描き込めるものを作ります。

将来的に、AIRアプリにしてクリップボード上のビットマップの貼り付けやテキスト入力、単純な図形の作成なども実装できればなかなか使えそうなものになるかと思います。

まず、今回描き込んでいく線を共有するために、Flex Media Serverと互換性のあるオープンソースのサーバ、Red5 を使います。

こちらは動作環境によってセットアップ方法がさまざまなので、ここでは導入については省略させて頂きます。

プログラム自体は、サーバ側クライアント側それぞれ必要になってきます。具体的なコードや仕組みなどについては、以下の通りです。

仕組み

おおまかな仕組みとしては、Flash側でマウスをドラッグした時にサーバ側へどこからどこへ移動したかを送り、サーバ側はそれを接続中のすべてのクライアントにそのまま送ります。

クライアントは上記の命令を受け取ると、それに従って線を描画します。

なお、自分で描いた線はそのまま描くようにした方が良いと思いますが、今回は区別なく一回サーバに送ってから表示されるようにしているので、ネットワークによっては遅延が発生することがあるかと思います。

クライアント側の処理

クライアント側は単純に、マウスが押されてからマウス移動イベントを監視して前のポインタ位置と移動後のポインタ位置をサーバへ送るだけの処理となっています。

実際のコードは以下のようになります。

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.net.Responder;
    import net.WhiteboardConnection;

    public class whiteboard extends Sprite
    {
        private var con:WhiteboardConnection = new WhiteboardConnection();
        private var canvas:Sprite;
        private var startPoint:Point = new Point();
        
        public function whiteboard()
        {
            init();
        }
        
        private function init():void
        {
            canvas = new Sprite();
            addChild(canvas);
            
            // 1px, 黒
            canvas.graphics.lineStyle(1, 0x000000);
            
            // 接続
            con.connect("rtmp://localhost/Whiteboard");
            con.setGraphics(canvas.graphics);
            
            // マウスイベントリスナ登録
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }
        
        private function onMouseDown(event:MouseEvent):void
        {
            startPoint.x = mouseX;
            startPoint.y = mouseY;
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        }
        
        private function onMouseUp(event:MouseEvent):void
        {
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        }
        
        private function onMouseMove(event:MouseEvent):void
        {
            con.call("drawLine", null, startPoint.x, startPoint.y, mouseX, mouseY);

            startPoint.x = mouseX;
            startPoint.y = mouseY;
        }
    }
}

また、サーバからそれらの座標を受け取り描画処理を行うクラスも用意します。
これは、上記コードで使っているWhiteboardConnectionというクラスになります。

package net
{
    import flash.net.NetConnection;
    import flash.display.Graphics;

    public class WhiteboardConnection extends NetConnection
    {
        private var graphics:Graphics;
        
        public function WhiteboardConnection()
        {
            super();
        }
        
        public function drawLine(startX:int, startY:int, endX:int, endY:int):void
        {
            graphics.moveTo(startX, startY);
            graphics.lineTo(endX, endY);
        }
        
        public function setGraphics(g:Graphics):void
        {
            graphics = g;
        }
        
        public function getGraphics():Graphics
        {
            return graphics;
        }
    }
}

サーバ側の処理

クライアント側の処理で接続時に「rtmp://localhost/Whiteboard」というURLに接続しているように、Red5のwebapps内にWhiteboardというアプリケーションを作成・配置しておく必要があります。

サーバ側の処理はどうすればよいかというと、クライアント側のコードを再度見て頂くと、con.call("drawLine", null, startPoint.x, startPoint.y, mouseX, mouseY); という行がonMouseMove()内にあるのが分かると思います。

これは、サーバ側の"drawLine"というメソッドをcallするという意味です。Red5で使うクラスはApplicationAdapterのサブクラスを使うので、以下のようなコードになります。

package jp.flup.whiteboard;

import java.util.Iterator;
import java.util.Set;

import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IServiceCapableConnection;

public class Application extends ApplicationAdapter
{
    // クライアント側から呼ばれるメソッド
    public String drawLine(IConnection conn, int startX, int startY, int endX, int endY)
    {
        IScope scope = Red5.getConnectionLocal().getScope();
        Set<IClient> clients = scope.getClients();
        Iterator<IClient> it = clients.iterator();
        Object[] params = new Object[]{startX, startY, endX, endY};
        while(it.hasNext())
        {
            IClient client = it.next();
            Set<IConnection> connset = client.getConnections();
            Iterator<IConnection> it_con = connset.iterator();
            
            while(it_con.hasNext())
            {
                IConnection connection = it_con.next();
                if(connection instanceof IServiceCapableConnection)
                {
                    IServiceCapableConnection sc = (IServiceCapableConnection)connection;
                    // クライアント側のメソッドを呼ぶ
                    sc.invoke("drawLine", params);
                }
            }
        }
        return "OK";
    }
}

IServiceCapableConnection.invoke() メソッドは第1パラメータで指定した名前のクライアント側のメソッドを呼び出します。第2パラメータはそのままクライアント側のメソッドに渡されます。

これは、前述のWhiteboardConnection.drawLine()メソッドになります。

最後に

実際に動作できるデモを用意したかったのですが、Red5が設置できる公開可能なサーバがなくて断念しました。。
暫定的に自宅サーバに置いてみました。以下から閲覧できると思います。
http://blog.flup.jp/whiteboard/

外から試せてないので、誰か実験お願いします(^^;

一通りのファイル一式をダウンロードできるようにしましたので、ローカル環境などでぜひ試してみてください。

whiteboard_client_080915.zip
whiteboard_server_080915.zip

LINEで送る
Pocket

Flexでホワイトボードアプリを作ってみる [1]」への1件のフィードバック

  1. はじめまして。公開されいているRED5のホワイトボードアプリをどうしても試してみたいのですが、環境設定の仕方がわかりません。超初心者で申し訳ございませんが、ファイルの配置場所等をお教えいただけませんでしょうか。Win版RED5は動かしていてでもアプリは動きます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です