iPhoneみたいなscrollをKinetic Scrollingって言うの?
Flashで、画像をドラッグ移動させるプログラムを作ったら「iPhoneみたいに惰性移動しないの?」って言われた。
まあ、仕様書にそう書いてないからねw
これだけ一般的になってるんだからググッてみれば、使えるコードも多いんじゃないかと思って調べてみたけど、意外と見つからない。
javascriptの方が、ヒットする件数は多いかな。
Flexのライブラリで、近い動きをするライブラリがあったので、Flash用に改造してみた。
完全に同じ動きさせるのは、凄く調整が大変なので、それっぽい動きをするだけです。
Main.as
package { import flash.display.Sprite; import flash.events.Event; import flash.geom.Rectangle; import manager.FlickDragManager; /** * 使う側のコード * @author takanemu */ public class Main extends Sprite { private var _manager:FlickDragManager = new FlickDragManager(); public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point var child:Sprite = new Sprite(); child.graphics.beginFill(0xFF0000); child.graphics.drawRect(0, 0, 200, 200); child.graphics.endFill(); child.x = 200; child.y = 200; addChild(child); _manager.target = child; _manager.dragRect = new Rectangle(10, 10, 780, 580); this.graphics.beginFill(0x222222, 1); this.graphics.drawRect(0, 0, this.stage.stageWidth, this.stage.stageHeight); } } }
FlickDragManager.as
package manager { import caurina.transitions.Tweener; import flash.display.DisplayObject; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.getTimer; /** * フリックドラッグマネージャー * KineticScrollManagerを参考に、改良を加えています。 * 元のコードのライセンスは、GNU Lesser GPLなので、それに準じます。 * @see http://code.google.com/p/nbflexlib/ * @author takanemu */ public class FlickDragManager { // 履歴保持数 private static const HISTORY_LENGTH:uint = 6; // ターゲットオブジェクト private var _target:DisplayObject; // 惰性移動値 private var _velocity:Point; // 引きずり履歴位置 private var _previousPoints:Vector.<Point> = new Vector.<Point>(); // 引きずり履歴時間 private var _previousTimes:Vector.<int> = new Vector.<int>(); // 移動範囲 public var dragRect:Rectangle; /** * ターゲットアクセッサー */ public function set target(value:DisplayObject):void { _target = value; if (target) { // マウスダウンイベントリスナー追加 target.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } } public function get target():DisplayObject { return _target; } /** * コンストラクタ */ public function FlickDragManager() { } /** * マウスボタン押下処理 * @param event イベント */ private function mouseDownHandler(event:MouseEvent):void { // 惰性移動強制停止 stop(); // 初回位置設定 _previousPoints = Vector.<Point>([new Point(event.stageX, event.stageY)]); _previousTimes = Vector.<int>([getTimer()]); // マウス移動イベントリスナー追加 target.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); // マウスボタン解除イベントリスナー追加 target.stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } /** * マウスカーソル移動処理 * @param event イベント */ private function mouseMoveHandler(event:MouseEvent):void { var currPoint:Point = new Point(event.stageX, event.stageY); var currTime:int = getTimer(); var previousPoint:Point = Point(_previousPoints[_previousPoints.length - 1]); var previousTime:int = int(_previousTimes[_previousTimes.length - 1]); var diff:Point = currPoint.subtract(previousPoint); target.x += diff.x; target.y += diff.y; _previousPoints.push(currPoint); _previousTimes.push(currTime); if (_previousPoints.length >= HISTORY_LENGTH) { _previousPoints.shift(); _previousTimes.shift(); } } /** * マウスボタン解除処理 * @param event イベント */ private function mouseUpHandler(event:MouseEvent):void { // 移動&ボタン解除のリスナーを解除 target.stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); target.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); // 惰性移動アニメーション用イベントリスナー追加 target.addEventListener(Event.ENTER_FRAME, enterFrameHandler); var currPoint:Point = new Point(event.stageX, event.stageY); var currTime:int = getTimer(); var firstPoint:Point = _previousPoints[0]; var firstTime:int = _previousTimes[0]; var diff:Point = currPoint.subtract(firstPoint); var time:Number = (currTime - firstTime) / (1000 / target.stage.frameRate); _velocity = new Point(diff.x / time, diff.y / time); } /** * 惰性移動処理 * @param event イベント */ private function enterFrameHandler(event:Event):void { _velocity.x *= 0.8; _velocity.y *= 0.8; if (Math.abs(_velocity.x) < 0.1) _velocity.x = 0; if (Math.abs(_velocity.y) < 0.1) _velocity.y = 0; if (!_velocity.x && !_velocity.y) { stop(); return; } target.x += _velocity.x; target.y += _velocity.y; } /** * 停止処理 */ public function stop():void { if (target.hasEventListener(Event.ENTER_FRAME)) { // 惰性移動アニメーション用のリスナーを解除 target.removeEventListener(Event.ENTER_FRAME, enterFrameHandler); if (dragRect.containsRect(new Rectangle(target.x, target.y, target.width, target.height)) == false) { // 移動範囲矩形から逸脱していたら戻す var px:Number = target.x; var py:Number = target.y; if (dragRect.left > target.x) { px = dragRect.left; } if (dragRect.right < (target.x + target.width)) { px = dragRect.right - target.width; } if (dragRect.top > target.y) { py = dragRect.top; } if (dragRect.bottom < (target.y + target.height)) { py = dragRect.bottom - target.height; } Tweener.removeTweens(target); Tweener.addTween(target, { x:px, y:py, useFrames:true, time:24, transition:"easeOutElastic" } ); } } _velocity = null; _previousPoints.length = 0; _previousTimes.length = 0; } } }