この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
前回に引き続き、自前ドラック機構を用いた Flex の Tips を解説します。
お題「回転ボタン」
描画ツールの変形機能などに存在する回転ボタンですが、Flex の Panel コンポーネントを拡張して、Panel 自身がクルクル回る機能を実装してみましょう。
Panel の回転は、行列 (Matrix) を用います。マウスが移動するたびに Panel の中心座標とマウス座標の 2 点から角度を算出し、この値を基に行列変換処理を行い、Panel の transform.matrix プロパティにセットすることで実現可能です。
実装コード (RotatablePanel.as)
package jp.classmethod.sample {
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import spark.components.Button;
import spark.components.Panel;
public class RotatablePanel extends Panel {
protected const PI:Number = Math.PI;
[SkinPart]
public var rotateButton :Button;
protected var startCenterX :int;
protected var startCenterY :int;
protected var startRadians :Number;
protected var startRotateMatrix :Matrix;
public function RotatablePanel() {
super();
minWidth = 200;
minHeight = 200;
maxWidth = 500;
maxHeight = 500;
setStyle("skinClass", RotatablePanelSkin);
}
protected override function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if(instance == rotateButton) {
rotateButton.addEventListener(MouseEvent.MOUSE_DOWN, rotateButtonMouseDownHandler);
}
}
protected function rotateButtonMouseDownHandler(event:MouseEvent):void {
var gp:Point;
var dx:int;
var dy:int
startRotateMatrix = transform.matrix;
gp = localToGlobal( new Point(width * 0.5, height * 0.5) );
startCenterX = gp.x;
startCenterY = gp.y;
dx = stage.mouseX - startCenterX;
dy = stage.mouseY - startCenterY;
startRadians = Math.atan2(dy, dx);
stage.addEventListener(MouseEvent.MOUSE_MOVE, stageRotateMouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, stageRotateMouseUpHandler);
}
protected function stageRotateMouseMoveHandler(event:MouseEvent):void {
var dx :int = stage.mouseX - startCenterX;
var dy :int = stage.mouseY - startCenterY;
var angle :Number = Math.atan2(dy, dx) - startRadians;
var m :Matrix = startRotateMatrix.clone();
var lp :Point = globalToLocal( new Point(startCenterX, startCenterY) );
rotateAroundInternalPoint(m, width * 0.5, height * 0.5, angle);
transform.matrix = m;
event.updateAfterEvent();
}
protected function stageRotateMouseUpHandler(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, stageRotateMouseMoveHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, stageRotateMouseUpHandler);
}
protected function rotateAroundInternalPoint(m:Matrix, x:Number, y:Number, radians:Number):void {
var p:Point;
p = new Point(x, y);
p = m.transformPoint(p);
m.tx = m.tx - p.x;
m.ty = m.ty - p.y;
m.rotate(radians);
m.tx = m.tx + p.x;
m.ty = m.ty + p.y;
}
}
}
実装コード (RotatablePanelSkin.mxml)
<?xml version="1.0" encoding="utf-8"?>
<s:Skin
xmlns:fx = "http://ns.adobe.com/mxml/2009"
xmlns:s = "library://ns.adobe.com/flex/spark"
mouseEnabled = "false"
minWidth = "131"
minHeight = "127"
alpha.disabled = "0.5"
alpha.disabledWithControlBar = "0.5"
>
<fx:Metadata>
<![CDATA[
[HostComponent("jp.classmethod.sample.RotatablePanel")]
]]>
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
<s:State name="normalWithControlBar" stateGroups="withControls" />
<s:State name="disabledWithControlBar" stateGroups="withControls" />
</s:states>
<s:Group left="0" right="0" top="0" bottom="0">
<s:Group id="topGroupMask" left="1" top="1" right="1" bottom="1">
<s:Rect id="topMaskRect" left="0" top="0" right="0" bottom="0">
<s:fill>
<s:SolidColor alpha="0" />
</s:fill>
</s:Rect>
</s:Group>
<s:Group id="bottomGroupMask" left="1" top="1" right="1" bottom="1"
includeIn="normalWithControlBar, disabledWithControlBar">
<s:Rect id="bottomMaskRect" left="0" top="0" right="0" bottom="0">
<s:fill>
<s:SolidColor alpha="0" />
</s:fill>
</s:Rect>
</s:Group>
<s:Rect id="border" left="0" right="0" top="0" bottom="0" >
<s:stroke>
<s:SolidColorStroke id="borderStroke" weight="1" />
</s:stroke>
</s:Rect>
<s:Rect id="background" left="1" top="1" right="1" bottom="1">
<s:fill>
<s:SolidColor id="backgroundFill" color="#FFFFFF" />
</s:fill>
</s:Rect>
<s:Group id="contents" left="1" right="1" top="1" bottom="1">
<s:layout>
<s:VerticalLayout gap="0" horizontalAlign="justify" />
</s:layout>
<s:Group id="topGroup" mask="{topGroupMask}">
<s:Rect id="tbFill" left="0" right="0" top="0" bottom="1">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xE2E2E2" />
<s:GradientEntry color="0xD9D9D9" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Rect id="tbHilite" left="0" right="0" top="0" bottom="0">
<s:stroke>
<s:LinearGradientStroke rotation="90" weight="1">
<s:GradientEntry color="0xEAEAEA" />
<s:GradientEntry color="0xD9D9D9" />
</s:LinearGradientStroke>
</s:stroke>
</s:Rect>
<s:Rect id="tbDiv" left="0" right="0" height="1" bottom="0">
<s:fill>
<s:SolidColor color="0xC0C0C0" />
</s:fill>
</s:Rect>
<s:Label
id="titleDisplay" left="9" right="3" top="1" bottom="0" minHeight="30"
maxDisplayedLines="1" verticalAlign="middle" textAlign="start" fontWeight="bold" />
</s:Group>
<s:Group id="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0" />
</s:Group>
<s:Button
id="rotateButton"
width="30" height="30" bottom="0" right="0"
buttonMode="true" chromeColor="0x000099" />
</s:Group>
</s:Skin>
実装コード (Main.mxml)
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx = "http://ns.adobe.com/mxml/2009"
xmlns:s = "library://ns.adobe.com/flex/spark"
xmlns:cm = "jp.classmethod.sample.*"
backgroundAlpha = "1"
backgroundColor = "0x333333"
>
<cm:RotatablePanel width="200" height="200" verticalCenter="0" horizontalCenter="-150">
<cm:layout>
<s:VerticalLayout paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="40" />
</cm:layout>
<s:Button width="100%" height="100%" />
<s:Button width="100%" height="100%" />
<s:Button width="100%" height="100%" />
<s:Button width="100%" height="100%" />
</cm:RotatablePanel>
<cm:RotatablePanel width="200" height="200" verticalCenter="0" horizontalCenter="150">
<cm:layout>
<s:VerticalLayout paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="40" />
</cm:layout>
<s:Button width="100%" height="100%" />
<s:Button width="100%" height="100%" />
<s:Button width="100%" height="100%" />
<s:Button width="100%" height="100%" />
</cm:RotatablePanel>
</s:Application>
出力結果
出力結果は次の通り
[SWF]http://publick-blog.s3.amazonaws.com/wp-content/uploads/2011/07/DragLesson03.swf,640,480[/SWF]