# 自前 Dragger #03

2011.07.29

## お題「回転ボタン」

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]