Forked from: clockmaker's BetweenAS3 Custom Easing Generator diff:1 forked from: BetweenAS3 Custom Easing Generator l.dijkman forked:0favorite:0lines:284license : MIT License modified : 2010-11-10 00:51:14 Embed Tweet // forked from clockmaker's BetweenAS3 Custom Easing Generator /** * [BetweenAS3 Custom Easing Generator] * * Please drag control points. * ベジェ曲線の制御点をドラッグすることで、 * カスタムイージングが作れます。 * * @author yasu * @see http://clockmaker.jp/ */ package { import com.bit101.components.*; import flash.display.*; import flash.events.*; import flash.geom.*; import org.libspark.betweenas3.*; import org.libspark.betweenas3.core.easing.*; import org.libspark.betweenas3.easing.*; import org.libspark.betweenas3.tweens.*; [SWF(width=465,height=465,frameRate=60)] public class BezierGraph extends Sprite { public static const GRAPH_RECT:Rectangle = new Rectangle(50, 60, 400, 300); public static const PREVIEW_RECT:Rectangle = new Rectangle(10, 430, 465 - 20, 20); public static const GRAPH_STEP:Number = 20; private var _controlA:BezierPoint = new BezierPoint(); private var _controlB:BezierPoint = new BezierPoint(); private var _pointA:BezierPoint = new BezierPoint(_controlA); private var _pointB:BezierPoint = new BezierPoint(_controlB); private var _container:Sprite = new Sprite(); private var _canvas:Sprite = new Sprite(); private var _division:Shape = new Shape(); private var _previewShape:Shape = new Shape(); private var _tween:ITween; public function BezierGraph(){ _container.addChild(_division); _container.addChild(_canvas); addChild(_container); addChild(_previewShape); drawDivision(); drawPreview(); initUI(); initPoints(); updateEase(); addEventListener(Event.ENTER_FRAME, loop); } public function loop(event:Event):void { _canvas.graphics.clear(); // control line drawLine(_pointA.toPoint(), _controlA.toPoint()); drawLine(_controlB.toPoint(), _pointB.toPoint()); // bezier init _canvas.graphics.moveTo(_pointA.x, _pointA.y); _canvas.graphics.lineStyle(1, 0xCC0000); // bezier loop var bezier:BezierSegment = new BezierSegment(_pointA.toPoint(), _controlA.toPoint(), _controlB.toPoint(), _pointB.toPoint()); for (var t:Number = 0.0; t <= 1.0; t += 0.01){ var pt:Point = bezier.getValue(t); _canvas.graphics.lineTo(pt.x, pt.y); } // bezier finish _canvas.graphics.lineTo(_pointB.x, _pointB.y); // preview current time t = _tween.position / _tween.duration; var xx:Number = t * GRAPH_RECT.width + GRAPH_RECT.left; drawLine(new Point(xx, GRAPH_RECT.bottom), new Point(xx, GRAPH_RECT.top), 0x555555); } /** * トゥイーンを作成 */ private function updateEase():void { if (_tween && _tween.isPlaying) _tween.stop(); // カスタムイージングを作成 var ease:IEasing = Custom.func(function(t:Number, b:Number, c:Number, d:Number):Number { var bezier:BezierSegment = new BezierSegment(_pointA.toNormalPoint(), _controlA.toNormalPoint(), _controlB.toNormalPoint(), _pointB.toNormalPoint()); return c * bezier.getYForX(t / d) + b; }); // トゥイーンを作成 _tween = BetweenAS3.tween(_previewShape, {x: PREVIEW_RECT.right - _previewShape.width}, {x: PREVIEW_RECT.x}, 4, ease); _tween.stopOnComplete = false; _tween.play(); } private function initUI():void { // BackGround graphics.beginFill(0x0); graphics.drawRect(0, 0, 465, 465); graphics.endFill(); // Label var l:Label = new Label(this, 5, -5, "BetweenAS3 Custom Easing Generator") l.scaleX = l.scaleY = 2; new Label(this, 8, GRAPH_RECT.top - 30, "-- EDITOR --"); new Label(this, 8, PREVIEW_RECT.top - 30, "-- PREVIEW --"); } private function initPoints():void { _container.addChild(_pointA); _container.addChild(_controlA); _container.addChild(_controlB); _container.addChild(_pointB); _pointA.x = GRAPH_RECT.left; _pointA.y = GRAPH_RECT.bottom; _controlA.x = 300; _controlA.y = 350; _controlB.x = 400; _controlB.y = 350; _pointB.x = GRAPH_RECT.right; _pointB.y = GRAPH_RECT.top; _pointA.mouseEnabled = false; _pointB.mouseEnabled = false; } private function drawPreview():void { _previewShape.graphics.beginFill(0x990000) _previewShape.graphics.drawRect(0, 0, 16, 16); _previewShape.y = PREVIEW_RECT.y + 2; _container.graphics.lineStyle(1, 0x333333); _container.graphics.drawRect(PREVIEW_RECT.x, PREVIEW_RECT.y, PREVIEW_RECT.width, PREVIEW_RECT.height); } private function drawLine(p0:Point, p1:Point, color:uint = 0xCCCCCC):void { _canvas.graphics.lineStyle(1, color); _canvas.graphics.moveTo(p0.x, p0.y); _canvas.graphics.lineTo(p1.x, p1.y); _canvas.graphics.lineStyle(); } private function drawDivision():void { _division.graphics.lineStyle(1, 0x333333); for (var i:int = 0; i <= GRAPH_RECT.width; i++){ if (i % GRAPH_STEP == 0){ _division.graphics.moveTo(i, 0); _division.graphics.lineTo(i, GRAPH_RECT.height); } if ((i / GRAPH_RECT.width * 100) % 10 == 0){ new Label(_container, i + GRAPH_RECT.left - 10, GRAPH_RECT.bottom + 10, "t=" + Math.round(i / GRAPH_RECT.width * 10) / 10); } } for (var j:int = 0; j <= GRAPH_RECT.height; j++){ if (j % GRAPH_STEP == 0){ _division.graphics.moveTo(0, j); _division.graphics.lineTo(GRAPH_RECT.width, j); } if ((j / GRAPH_RECT.height * 100) % 10 == 0){ new Label(_container, 10, j + GRAPH_RECT.top - 8, Math.round((1 - j / GRAPH_RECT.height) * 100) + "%"); } } _division.x = GRAPH_RECT.x; _division.y = GRAPH_RECT.y; } } } import flash.display.*; import flash.events.*; import flash.geom.*; import org.libspark.betweenas3.core.easing.*; internal class BezierPoint extends Sprite { private const COLOR:int = 0xFFFFFF; private const RADIUS:int = 4; private var _diffA:Point = new Point(); private var _diffB:Point = new Point(); public var childA:Sprite; public var childB:Sprite; public function BezierPoint(childA:Sprite = null):void { this.childA = childA; graphics.beginFill(COLOR); graphics.drawCircle(0, 0, RADIUS); graphics.endFill(); buttonMode = true; addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } private function mouseDownHandler(event:*):void { startDrag(false, BezierGraph.GRAPH_RECT); if (childA){ _diffA.x = childA.x - x; _diffA.y = childA.y - y; } stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } private function mouseMoveHandler(event:MouseEvent):void { if (childA){ childA.x = x + _diffA.x; childA.y = y + _diffA.y; } event.updateAfterEvent(); } private function mouseUpHandler(event:MouseEvent):void { stopDrag(); stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } public function toPoint():Point { return new Point(this.x, this.y); } public function toNormalPoint():Point { var xx:Number = (this.x - BezierGraph.GRAPH_RECT.left) / BezierGraph.GRAPH_RECT.width; var yy:Number = (BezierGraph.GRAPH_RECT.height - this.y + BezierGraph.GRAPH_RECT.top) / BezierGraph.GRAPH_RECT.height; return new Point(xx, yy); } } // Flash Professional同梱flパッケージのBezierSegmentクラスをコピペしました // Adobeさんすみません。まずかったら消しますので (><; // Copyright ゥ 2007. Adobe Systems Incorporated. All Rights Reserved. /** * A Bezier segment consists of four Point objects that define a single cubic Bezier curve. * The BezierSegment class also contains methods to find coordinate values along the curve. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword BezierSegment, Copy Motion as ActionScript * @see ../../motionXSD.html Motion XML Elements */ class BezierSegment { /** * The first point of the Bezier curve. * It is a node, which means it falls directly on the curve. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public var a:Point; /** * The second point of the Bezier curve. * It is a control point, which means the curve moves toward it, * but usually does not pass through it. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public var b:Point; /** * The third point of the Bezier curve. * It is a control point, which means the curve moves toward it, * but usually does not pass through it. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public var c:Point; /** * The fourth point of the Bezier curve. * It is a node, which means it falls directly on the curve. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public var d:Point; /** * Constructor for BezierSegment instances. * * @param a The first point of the curve, a node. * * @param b The second point of the curve, a control point. * * @param c The third point of the curve, a control point. * * @param d The fourth point of the curve, a node. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript * @see #propertyDetail property details */ function BezierSegment(a:Point, b:Point, c:Point, d:Point){ this.a = a; this.b = b; this.c = c; this.d = d; } /** * Calculates the location of a two-dimensional cubic Bezier curve at a specific time. * * @param t The <code>time</code> or degree of progress along the curve, as a decimal value between <code>0</code> and <code>1</code>. * <p><strong>Note:</strong> The <code>t</code> parameter does not necessarily move along the curve at a uniform speed. For example, a <code>t</code> value of <code>0.5</code> does not always produce a value halfway along the curve.</p> * * @return A point object containing the x and y coordinates of the Bezier curve at the specified time. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public function getValue(t:Number):Point { var ax:Number = this.a.x; var x:Number = (t * t * (this.d.x - ax) + 3 * (1 - t) * (t * (this.c.x - ax) + (1 - t) * (this.b.x - ax))) * t + ax; var ay:Number = this.a.y; var y:Number = (t * t * (this.d.y - ay) + 3 * (1 - t) * (t * (this.c.y - ay) + (1 - t) * (this.b.y - ay))) * t + ay; return new Point(x, y); } /** * Calculates the value of a one-dimensional cubic Bezier equation at a specific time. * By contrast, a Bezier curve is usually two-dimensional * and uses two of these equations, one for the x coordinate and one for the y coordinate. * * @param t The <code>time</code> or degree of progress along the curve, as a decimal value between <code>0</code> and <code>1</code>. * <p><strong>Note:</strong> The <code>t</code> parameter does not necessarily move along the curve at a uniform speed. For example, a <code>t</code> value of <code>0.5</code> does not always produce a value halfway along the curve.</p> * * @param a The first value of the Bezier equation. * * @param b The second value of the Bezier equation. * * @param c The third value of the Bezier equation. * * @param d The fourth value of the Bezier equation. * * @return The value of the Bezier equation at the specified time. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public static function getSingleValue(t:Number, a:Number = 0, b:Number = 0, c:Number = 0, d:Number = 0):Number { return (t * t * (d - a) + 3 * (1 - t) * (t * (c - a) + (1 - t) * (b - a))) * t + a; } /** * Finds the <code>y</code> value of a cubic Bezier curve at a given x coordinate. * Some Bezier curves overlap themselves horizontally, * resulting in more than one <code>y</code> value for a given <code>x</code> value. * In that case, this method will return whichever value is most logical. * * Used by CustomEase and BezierEase interpolation. * * @param x An x coordinate that lies between the first and last point, inclusive. * * @param coefficients An optional array of number values that represent the polynomial * coefficients for the Bezier. This array can be used to optimize performance by precalculating * values that are the same everywhere on the curve and do not need to be recalculated for each iteration. * * @return The <code>y</code> value of the cubic Bezier curve at the given x coordinate. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, Copy Motion as ActionScript */ public function getYForX(x:Number, coefficients:Array = null):Number { // Clamp to range between end points. // The padding with the small decimal value is necessary to avoid bugs // that result from reaching the limits of decimal precision in calculations. // We have tests that demonstrate this. if (this.a.x < this.d.x){ if (x <= this.a.x + 0.0000000000000001) return this.a.y; if (x >= this.d.x - 0.0000000000000001) return this.d.y; } else { if (x >= this.a.x + 0.0000000000000001) return this.a.y; if (x <= this.d.x - 0.0000000000000001) return this.d.y; } if (!coefficients){ coefficients = getCubicCoefficients(this.a.x, this.b.x, this.c.x, this.d.x); } // x(t) = a*t^3 + b*t^2 + c*t + d var roots:Array = getCubicRoots(coefficients[0], coefficients[1], coefficients[2], coefficients[3] - x); var time:Number = NaN; if (roots.length == 0) time = 0; else if (roots.length == 1) time = roots[0]; else { for each (var root:Number in roots){ if (0 <= root && root <= 1){ time = root; break; } } } if (isNaN(time)) return NaN; var y:Number = getSingleValue(time, this.a.y, this.b.y, this.c.y, this.d.y); return y; } /** * Calculates the coefficients for a cubic polynomial equation, * given the values of the corresponding cubic Bezier equation. * * @param a The first value of the Bezier equation. * * @param b The second value of the Bezier equation. * * @param c The third value of the Bezier equation. * * @param d The fourth value of the Bezier equation. * * @return An array containing four number values, * which are the coefficients for a cubic polynomial. * The coefficients are ordered from the highest degree to the lowest, * so the first number in the array would be multiplied by t^3, the second by t^2, and so on. * * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript * @see #getCubicRoots() */ public static function getCubicCoefficients(a:Number, b:Number, c:Number, d:Number):Array { return [-a + 3 * b - 3 * c + d, 3 * a - 6 * b + 3 * c, -3 * a + 3 * b, a]; } /** * Finds the real solutions, if they exist, to a cubic polynomial equation of the form: at^3 + bt^2 + ct + d. * This method is used to evaluate custom easing curves. * * @param a The first coefficient of the cubic equation, which is multiplied by the cubed variable (t^3). * * @param b The second coefficient of the cubic equation, which is multiplied by the squared variable (t^2). * * @param c The third coefficient of the cubic equation, which is multiplied by the linear variable (t). * * @param d The fourth coefficient of the cubic equation, which is the constant. * * @return An array of number values, indicating the real roots of the equation. * There may be no roots, or as many as three. * Imaginary or complex roots are ignored. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public static function getCubicRoots(a:Number = 0, b:Number = 0, c:Number = 0, d:Number = 0):Array { // make sure we really have a cubic if (!a) return BezierSegment.getQuadraticRoots(b, c, d); // normalize the coefficients so the cubed term is 1 and we can ignore it hereafter if (a != 1){ b /= a; c /= a; d /= a; } var q:Number = (b * b - 3 * c) / 9; // won't change over course of curve var qCubed:Number = q * q * q; // won't change over course of curve var r:Number = (2 * b * b * b - 9 * b * c + 27 * d) / 54; // will change because d changes // but parts with b and c won't change // determine if there are 1 or 3 real roots using r and q var diff:Number = qCubed - r * r; // will change if (diff >= 0){ // avoid division by zero if (!q) return [0]; // three real roots var theta:Number = Math.acos(r / Math.sqrt(qCubed)); // will change because r changes var qSqrt:Number = Math.sqrt(q); // won't change var root1:Number = -2 * qSqrt * Math.cos(theta / 3) - b / 3; var root2:Number = -2 * qSqrt * Math.cos((theta + 2 * Math.PI) / 3) - b / 3; var root3:Number = -2 * qSqrt * Math.cos((theta + 4 * Math.PI) / 3) - b / 3; return [root1, root2, root3]; } else { // one real root var tmp:Number = Math.pow(Math.sqrt(-diff) + Math.abs(r), 1 / 3); var rSign:int = (r > 0) ? 1 : r < 0 ? -1 : 0; var root:Number = -rSign * (tmp + q / tmp) - b / 3; return [root]; } return []; } /** * Finds the real solutions, if they exist, to a quadratic equation of the form: at^2 + bt + c. * * @param a The first coefficient of the quadratic equation, which is multiplied by the squared variable (t^2). * * @param b The second coefficient of the quadratic equation, which is multiplied by the linear variable (t). * * @param c The third coefficient of the quadratic equation, which is the constant. * * @return An array of number values, indicating the real roots of the equation. * There may be no roots, or as many as two. * Imaginary or complex roots are ignored. * @playerversion Flash 9.0.28.0 * @playerversion AIR 1.0 * @productversion Flash CS3 * @langversion 3.0 * @keyword Bezier curve, node, Copy Motion as ActionScript */ public static function getQuadraticRoots(a:Number, b:Number, c:Number):Array { var roots:Array = []; // make sure we have a quadratic if (!a){ if (!b) return []; roots[0] = -c / b; return roots; } var q:Number = b * b - 4 * a * c; var signQ:int = (q > 0) ? 1 : q < 0 ? -1 : 0; if (signQ < 0){ return []; } else if (!signQ){ roots[0] = -b / (2 * a); } else { roots[0] = roots[1] = -b / (2 * a); var tmp:Number = Math.sqrt(q) / (2 * a); roots[0] -= tmp; roots[1] += tmp; } return roots; } } Code Fullscreen Preview Fullscreen