BONKURA BLOG

仕事のメモ帳ブログ。いまのところActionScriptメイン。たまにCSSとかJavaScriptとか。

AS3版スクロールバー

いざというときに悩みそうなのでAS3でスクロールバーを作ってみました。
AS2よりコードがすっきりしていてわかりやすいです。

参考にしたのはこのサイト様です。
gotoandlearn.com - Object-Oriented Scrollbar 1
gotoandlearn.com - Object-Oriented Scrollbar 2
trick7.com blog: Flashスクロールバーのサンプル(flaファイル付き)

ただ、人様のサンプルを流用するだけでは芸が無いので、ちょっと機能を付加してみました。
付加した機能はこんな感じ。

・スクロールハンドルの後ろのバーを押すとその位置までハンドルが移動。
・マスクよりコンテンツのサイズが小さい場合はスクロールの表示なし。
・初期設定でユーザビリティ対策の初期動作のオン、オフの切り替え可能。
・初期設定でハンドルのサイズをコンテンツとの相対にするか決め打ちにするか設定可能。
・Win、Macの両方でマウスホイール稼働。

イージングはTweenerを利用しています。作っていてAS3が便利だなあ、と感じたのはやっぱりカスタムイベント。Tweenerとカスタムイベントの利用でAS2だとあれだけ面倒だったスクロールバーの実装が非常に簡潔になりました。

以下解説です。ムービークリップは何個か入れ子にしていて、説明が面倒なので以下からダウンロードしてご参考ください。
scrollbar.fla
あ、Tweenerにクラスパスが通っていることが前提です。クラスパスについては以下のサイト様をご参照ください。
FN0311006 - クラスパスの考え方 - Flash : テクニカルノート

ムービークリップscrollbarにリンケージしたクラスはコレ。
ここでは主にハンドルの挙動を制御します。ハンドルが動いたら他のクラスに「ハンドルが動いたよ!」とイベントを発信してあげます。

package  
{
	import com.pixelbreaker.ui.osx.MacMouseWheel;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	public class ScrollBar extends MovieClip
	{
		private var yMin:Number;
		private var yMax:Number;
		private var yOffset:Number;
		private var friction:Number = 0.2;
		
		public function ScrollBar() 
		{	
			
		}
		
		public function start(initMode:Boolean=false, resizeTH:Boolean=false):void
		{
			if (initMode == true) 
			{
				initMove();
			}
			else 
			{
				setup();
			}
			
			if (resizeTH == true) 
			{
				var scrollBox:MovieClip = parent as MovieClip;
				
				var mPc:Number = scrollBox.masker.height / scrollBox.content.height;
				thumb.height = track.height * mPc;
			}
			
			yMin = 0;
			yMax = track.height - thumb.height;
		}
		
		public function initMove():void
		{
			thumb.addEventListener(Event.ENTER_FRAME, xInitMoveHandler);
		}
		
		private function setup():void
		{
			thumb.buttonMode = true;
			thumb.addEventListener(MouseEvent.MOUSE_DOWN, thumbDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, thumUp);
			
			track.addEventListener(MouseEvent.MOUSE_DOWN, trackDown);
			
			var sb:MovieClip = thumb.parent.parent.parent as MovieClip;
			sb.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			
			//Mac用スクロール
			MacMouseWheel.setup(stage);
		}
		
		private function thumbDown(e:MouseEvent):void 
		{
			stage.addEventListener(MouseEvent.MOUSE_MOVE, thumbMove);
			yOffset = mouseY - thumb.y;
		}

		private function thumUp(e:MouseEvent):void 
		{
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, thumbMove);
			thumb.removeEventListener(Event.ENTER_FRAME, trackEnterframeHandler);
		}
				
		private function thumbMove(e:MouseEvent):void 
		{
			thumb.y = mouseY - yOffset;
			mouseLink();
			e.updateAfterEvent();
		}
		
		private function trackDown(e:MouseEvent):void
		{
			thumb.addEventListener(Event.ENTER_FRAME, trackEnterframeHandler);
		}
		
		private function trackEnterframeHandler(e:Event):void 
		{
			var dis:Number = mouseY - thumb.y;
			thumb.y += dis * friction;
			mouseLink();
			if (mouseY > yMax) 
			{
				var dis2:Number = yMax - thumb.y;
				if (dis2 <= 0.5) 
				{
					thumb.y = yMax;
				}
				thumb.y += dis2 * friction;
			}
			scrollEventCall();
		}
		
		private function mouseWheelHandler(e:MouseEvent):void
		{
			thumb.y = thumb.y - e.delta * 2;
			mouseLink();
			e.updateAfterEvent();
		}
		
		private function mouseLink():void
		{
			if (thumb.y <= yMin) 
			{
				thumb.y = yMin;
			}
			if (thumb.y >= yMax) 
			{
				thumb.y = yMax;
			}
			scrollEventCall();
		}
		
		private function scrollEventCall():void
		{
			var v:Number = thumb.y / yMax;
			dispatchEvent(new ScrollBarEvent(ScrollBarEvent.VALUE_CHANGED, v));
		}
		
		private function xInitMoveHandler(e:Event):void
		{
			var dis:Number = yMax - thumb.y;
			if (dis <= 0.5) 
			{
				thumb.y = yMax;
				thumb.removeEventListener(Event.ENTER_FRAME, xInitMoveHandler);
				thumb.addEventListener(Event.ENTER_FRAME, xInitRemoveHandler);
			}
			thumb.y += dis * friction;
			scrollEventCall();
		}
		
		private function xInitRemoveHandler(e:Event):void
		{
			var dis:Number = yMin - thumb.y;
			thumb.y += dis * friction;
			
			scrollEventCall();
			if (thumb.y <= 1) 
			{
				thumb.y = yMin;
				thumb.removeEventListener(Event.ENTER_FRAME, xInitRemoveHandler);
				setup();
			}
		}
		
	}
	
}

Mac用のスクロールはFlashだけでは実装できません。JavaScriptを利用します。詳しくはこちらのサイトをご参照ください。
pixelbreaker : AS3.0 MouseWheel on Mac OS X

次にその発信されたイベントをコンテンツ側で受け取って、ハンドルの動きと連動させます。ムービークリップscrollboxにリンケージしてください。

package  
{
	import caurina.transitions.Tweener;
	import flash.display.MovieClip;
	
	public class ScrollBox extends MovieClip
	{	
		public function ScrollBox() 
		{
			if (masker.height > content.height) 
			{
				sb.visible = false;
			}
			sb.addEventListener(ScrollBarEvent.VALUE_CHANGED, sbChange);
		}
		
		private function sbChange(e:ScrollBarEvent):void 
		{
			Tweener.addTween(content, { y:( -e.v * (content.height - masker.height)), time:1 } );
		}
		
	}
	
}

最後に発信するイベントの設定。Flashデフォのイベントではなく、ユーザーが設定したカスタムイベントを使用します。カスタムイベントにすることで、イベントに独自のプロパティを持たせることができます(この場合はハンドルのY位置が稼働最大域の何%の場所にあるかのパーセンテージ)。

package  
{
	import flash.events.Event;
	
	public class ScrollBarEvent extends Event
	{
		public static const VALUE_CHANGED:String = "value_changed";
		public var v:Number;
		
		public function ScrollBarEvent(type:String, v:Number) 
		{
			super(type);
			this.v = v;
		}
		
		public override function clone():Event
		{
			return new ScrollBarEvent(type, v);
		}
		
		public override function toString():String
		{
			return formatToString("ScrollBarEvent", "type", "bubbles", "cancelable", "v");
		}
		
	}
	
}

普通はムービークリップを置いただけですぐにスクロールが可能にしておけばいいのですが、Flashサイトでは「何かのイベントが終わったらスクロールを動かしたい」という場合が想定されます。その為、メインタイムライン等で関数を実行しないと、スクロールが始まらないようにしました。
その関数を実行する際の引数で、初期動作のon/off、ハンドルの高さの設定を相対か決め打ちにするかを決めることができます。
メインタイムラインに書くスクリプトはこんな感じ。

main_mc.sb.start(true,true);

最初の引数は初期動作の設定。falseにすると初期動作は実行されずにすぐにスクロールバーを利用することができます。
2つめの引数はハンドルの高さの設定です。コンテンツの長さに合わせて設定させたい場合はtrue、自分で決めたサイズで使いたい場合はfalseにしてください。

初期値は両方ともfalseに設定してあるので、引数を書かない場合は、初期動作なし&スクロールバーの高さは自分で設定した高さ、で表示されます。

一応ちゃんとできているように見えますが、まだ実戦で使用したわけでないので何かしらエラーがあるかもしれません。上手く動かないよ!っていう場合はコメントいただければと思います。

2008年10月9日 追伸
マウスホイールの動作がちょっと変です。Firefoxだとブラウザのスクロールバーが動かなくなってしまい、IEだと両方とも動いてしまいます。現在調査中。
なので利用する際はマウスホイールの箇所を切ったほうがいいかもしれません。

このエントリーをはてなブックマークに追加 Save This Page to del.icio.us
  1. ojiboss

    scrollbar.fla
    のリンクが切れていまして、サンプルをダウンロードできません。
    よろしくお願いします。

  2. Anonymous

    scrollbar.fla
    のリンクが切れていまして、サンプルをダウンロードできません。
    よろしくお願いします。

  3. Anonymous

    Flaファイルをサーバ移転の際に消してしまったようなので、こちらをご参照ください。
    http://www.gotoandlearn.com/play?id=71

    僕のはコレをもとに拡張しただけです。