AS3Graphics#7 ネイティブマウスカーソルのカスタマイズ

Flash アプリケーションで、マウスカーソルを任意のものに変更する方法について解説します。

以前から行われている一般的な方法として、マウスカーソルを非表示にし、代わりに自分で作成したマウスカーソルの表示オブジェクトを、ステージ上でマウスポインタの位置に追随させて動かすというものがあります。Flex アプリケーションの CursorManager クラスを使用した方法も、内部的にはこれと同じです。

package
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.ui.Mouse;
	
	/**
	 * 伝統的なマウスカーソルマスタマイズサンプル
	 * @author ishigami.shintaro
	 */
	public class ClassicMouseCursorTest extends Sprite
	{
		/**
		 * コンストラクタ。
		 */
		public function ClassicMouseCursorTest()
		{
			super();
			
			mouseCursor = new Shape();
			var g:Graphics;
			g = graphics;
			g.beginFill(0xCCCCCC);
			g.drawRect(0, 0, 400, 200);
			g.endFill();
			g = mouseCursor.graphics
			g.beginFill(0x000000);
			g.drawRect(-5, -5, 10, 10);
			g.endFill();
			mouseCursor.addEventListener(Event.ENTER_FRAME, mouseCursorEnterFrameHandler);
			addChild(mouseCursor);
			Mouse.hide();
			stage.addEventListener(MouseEvent.MOUSE_MOVE, stageMouseMoveHandler);
		}
		
		/**
		 * マウスカーソルの代わりに表示するShape。
		 */
		private var mouseCursor:Shape;
		
		/**
		 * mouseCursorのenterFrameイベントハンドラ。
		 * @param event
		 */
		private function mouseCursorEnterFrameHandler(event:Event):void {
			mouseCursor.rotation += 12;
		}
		
		/**
		 * stageのmouseMoveイベントハンドラ。
		 * @param event
		 */
		private function stageMouseMoveHandler(event:MouseEvent):void {
			mouseCursor.x = event.stageX;
			mouseCursor.y = event.stageY;
		}
	}
}

実行結果

[SWF]http://public-blog-dev.s3.amazonaws.com/wp-content/uploads/2011/12/ClassicMouseCursorTest.swf, 400, 200[/SWF]

Flash の上でマウスカーソルの形状が変わりますが、動かしてみて何か違和感があると思いませんか?
これは、実際のマウスカーソルの動きと、マウスカーソルの位置に追随している表示オブジェクトの動きに、Flash のフレームレートによる若干の遅延があるためです。フレームレートに依存しているということは、Flash 上で何か重い処理を行った場合などには、動きが止まってしまうこともあるということです。重い処理を行う際に、表示したビジーカーソルのアニメーションが止まってしまう等の現象はありがちです。また、Flash の領域外にカーソルを移動させると、領域外に出る直前にカーソルが表示されていた場所に残ってしまいます。Flash 領域外のマウス移動のイベントは、Flash からは取れないためです。

Flash Player 10.2 から、これらの問題点を解消した、新しいマウスカーソルのカスタマイズ方法が実装されました。では、その新しい方法を使って、再度同じ機能を実装してみます。

package { import flash.display.BitmapData; import flash.display.Graphics; import flash.display.Shape; import flash.display.Sprite; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.ui.Mouse; import flash.ui.MouseCursorData;

/** * ネイティブマウスカーソルのカスタマイズサンプル。 * @author ishigami.shintaro */ public class NativeMouseCursorTest extends Sprite { /** * カーソル名に使用する文字列。 */ private static const CURSOR_NAME:String = "sample"; /** * コンストラクタ。 */ public function NativeMouseCursorTest() { super(); var shape:Shape = new Shape(); var g:Graphics; g = graphics; g.beginFill(0xCCCCCC); g.drawRect(0, 0, 400, 200); g.endFill(); g = shape.graphics; g.beginFill(0x000000); g.drawRect(-5, -5, 10, 10); g.endFill(); var sprite:Sprite = new Sprite(); sprite.addChild(shape); shape.x = shape.y = 16; var mouseCursorData:MouseCursorData = new MouseCursorData(); var v:Vector. = new Vector.(30); for(var i:uint = 0; i < 30; i++) { //12°ずつ回転させながら30コマ分のBitmapImageを作成する。 shape.rotation = i * 12; v[i] = new BitmapData(32, 32, true, 0x000000); v[i].draw(sprite, null, null, null, new Rectangle(0, 0, 32, 32), true); } mouseCursorData.data = v; mouseCursorData.frameRate = 30; mouseCursorData.hotSpot = new Point(16, 16); Mouse.registerCursor(CURSOR_NAME, mouseCursorData); Mouse.cursor = CURSOR_NAME; } } } [/as3]

実行結果

[SWF]http://public-blog-dev.s3.amazonaws.com/wp-content/uploads/2011/12/NativeMouseCursorTest.swf, 400, 200[/SWF]

Flash 上でのマウスカーソルの動きが、最初のサンプルと比べてずっとスムーズになっています。また、Flash 表示領域ギリギリまでカーソルを持って行くと、カスタマイズされたカーソルが Flash の描画エリアの外にもはみ出して表示されている事がわかります。Flash 領域外にカーソルを移動させても、元の場所に残ったりすることはありません。これは、OS のネイティブなマウスカーソルを直接変更しているためです。

それでは内容を解説します。まず、MouseCurosrData クラスのインスタンスを作成し、ネイティブマウスカーソルとして表示するためデータを定義します。MouseCurosrData は、32 x 32 ピクセル以下の BitmapData の Vector、アニメーションのフレームレート、ホットスポットの座標で構成されます。
Vectorの要素は、マウスカーソルのアニメーションの1コマずつのビットマップです。アニメーションさせない場合の要素数は1です。
フレームレートには任意の値を指定できますが、あまり大きな値ではうまく処理されないかもしれません。Flash アプリケーション自体のフレームレートとは相互関係はありません。
ホットスポットはカーソルの当たり判定の位置で、例えば矢印状のカーソルであれば、矢印の先端座標を設定します。このサンプルでは、回転する四角形の中央に設定しています。
次に、作成した MouseCursorData を、Mouse.registerCursor() で ネイティブマウスカーソルに登録します。この時、このカーソルの名前を指定します。これでこのカーソルを表示する準備が整いました。あとは、Mouse.cursor に、登録時に指定したカーソル名を代入すると、作成したカーソルが表示されます。元に戻す場合は、MouseCursor.AUTO を代入します。

注意点として、Flex の CursorManager  によるカーソルの指定と競合した場合、ネイティブマウスカーソルは非表示にされてしまうので、CursorManager  のカーソルに負けてしまいます。