Flex / ActionScript で破線を描画するコンポーネント

なんか無いらしいので自作した。

# コンポーネントの実装

とりあえず Line を継承した DashedLine って名前のコンポーネントを作る。
DashedLine では draw() メソッドをオーバーライドされていて、Line で実線だったところが破線となるように実装されている。

```mxml:DashedLine.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Line xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" 
		xmlns:mx="library://ns.adobe.com/flex/mx">
	<fx:Declarations>
		<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			/** 破線の長さ */
			[Bindable]
			public var dashLength:uint;
			
			/**
			 * 破線を描画する
			 */
			override protected function draw(g:Graphics):void
			{
				// Our bounding box is (x1, y1, x2, y2)
				var x1:Number = measuredX + drawX;
				var y1:Number = measuredY + drawY;
				var x2:Number = measuredX + drawX + width;
				var y2:Number = measuredY + drawY + height;    
				
				// Which way should we draw the line?
				if ((xFrom <= xTo) == (yFrom <= yTo))
				{ 
					// top-left to bottom-right
					lineFromTo( g, x1, y1, x2, y2 );
				}
				else
				{
					// bottom-left to top-right
					lineFromTo( g, x1, y2, x2, y1 );
				}
			}
			
			/**
			 * dashLength で定義されている距離づつ移動し破線を引く
			 */
			private function lineFromTo(g:Graphics, x1:Number, y1:Number, x2:Number, y2:Number):void
			{
				var startPoint:Point = new Point(x1, y1);
				var endPoint:Point = new Point(x2, y2);
				
				if ( dashLength < 0 )
				{
					throw "dashLength を設定してください。";
				}
				
				// 全体の長さを求める
				var lineLength:Number = Point.distance( startPoint, endPoint );
				
				// 開始位置に移動
				g.moveTo( startPoint.x, startPoint.y );
				
				// 全体の長さが破線の長さ以下の場合は実践のみを引く
				if ( lineLength <= dashLength ) {
					g.lineTo( endPoint.x, endPoint.y );
					return;
				}
				
				// 1回に移動する距離を求める
				var step:Number = dashLength / lineLength;
				var interpolate:Point = Point.interpolate( endPoint, startPoint, step );
				var stepPoint:Point = interpolate.subtract( startPoint );
				
				// 移動した距離
				var movedSteps:Number = 0;
				var dashPoint:Point;
				
				// step が示す距離を移動しつつ直線を引く
				while ( movedSteps < 1 )
				{
					// 破線の終了位置を求める
					movedSteps += step;
					dashPoint = startPoint.add( stepPoint );
					
					if ( movedSteps > 1 )
					{
						// 破線の終了位置が全体の長さを超えないように調整
						g.lineTo( endPoint.x, endPoint.y );
						break;
					}
					
					// 線を引く
					g.lineTo( dashPoint.x, dashPoint.y );
					
					// 次の開始位置に移動（線を引かずに移動）
					movedSteps += step;
					startPoint = dashPoint.add( stepPoint );
					if ( movedSteps > 1 )
					{
						// 次の破線の開始位置が全体の長さを超えないように処理を終了する
						break;
					}
					g.moveTo( startPoint.x, startPoint.y );
				}
			}
		]]>
	</fx:Script>
</s:Line>
```

# 使用例

以下のように、 Graphic タグの中で、普通の Line と同じように使用する、stroke で線の色や太さを指定するのも同じ。
違うところは、<code>dashLength</code> で 破線の長さを指定しているところだけ。

```mxml:Lines.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:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:components="components.*">
	<fx:Declarations>
		<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
	</fx:Declarations>
	<s:Graphic>
		<!-- 実線 -->
		<s:Line x="20" y="20" width="300" height="200">
			<s:stroke>
				<s:SolidColorStroke color="#000000" weight="3" />
			</s:stroke>
		</s:Line>
		<!-- 破線 -->
		<components:DashedLine x="20" y="40" width="300" height="200" dashLength="8">
			<components:stroke>
				<s:SolidColorStroke color="#0000ff" weight="3" caps="square" />
			</components:stroke>
		</components:DashedLine>
	</s:Graphic>
</s:Application>
```

実行結果は以下のようになる。

![kobito.1374659567.238606.png](https://qiita-image-store.s3.amazonaws.com/0/8219/dd343396-7ef2-a462-27ba-19bf9f0c984d.png "kobito.1374659567.238606.png")