views:

1538

answers:

3

I'm trying to animate a round rectangle in ActionScript 3.

I want it to appear in the centre of the screen, and then grow out quickly in all four directions. Initial size should be about 30x10 pixels and final size about 300x100 pixels. The animation should take somewhere between 500 and 1000 milliseconds. I'd like the box to slightly outgrow these dimensions in the last few frames and then bounce back to the right size.

I'm now using a Back easeOut tween and a scale9Grid, thanks to TypeOneError's suggestion, however I'm still not out of the woods. I can get the box to bounce out right and downwards, and the animation looks right, except I would like the box to remain centered. The function ScaleFromCenterConstant is almost right, as the box remains centered, however if I send through the scale calculated by the tween the box explodes off the screen.

Should I be using Matrix.scale() or should I be setting Matrix.a .d .tx and .ty individually?

Here's my code thus far :

package {

import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;

import flash.display.*;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.utils.setTimeout;

public class Scaling extends Sprite
{

 private var startWidth:int = 50;
 private var startHeight:int = 30;
 private var startLineWidth:int = 5;
 private var startEllipse:int = 15;
 private var startCenter:Point = new Point(400, 300);

 public var mySprite:Sprite = new Sprite();
 public var myTween:Tween;

 public function Scaling() {
  this.stage.scaleMode = StageScaleMode.NO_SCALE;
  this.stage.align = StageAlign.TOP_LEFT;

  graphics.beginFill(0x000000);
  graphics.drawRect(0, 0, 800, 600);
  mySprite.graphics.endFill();

  mySprite.graphics.beginFill(0xF7F3DC);
  mySprite.graphics.lineStyle(startLineWidth, 0xD35F72);
  mySprite.graphics.drawRoundRect(0, 0, startWidth, startHeight, startEllipse, startEllipse);
  mySprite.graphics.endFill();

  mySprite.x = startCenter.x - startWidth / 2;
  mySprite.y = startCenter.y - startHeight / 2;
  mySprite.width = startWidth;
  mySprite.height = startHeight;

  mySprite.scale9Grid = new Rectangle(10, 10, 30, 10);

  setTimeout(GoGoGadgetScaling, 1000);
 }

 private function GoGoGadgetScaling():void {
  addChild(mySprite);

  var o:Object = new Object();
  o["scale"] = 1;

  myTween = new Tween(o, "scale", Back.easeOut, 1, 5, 0.3, true);

  myTween.addEventListener(TweenEvent.MOTION_CHANGE, function(evt:TweenEvent):void {
   //ScaleRightAndDown(mySprite, o["scale"] * 2, o["scale"], startCenter);
   //ScaleFromCenterConstant(mySprite, 1.2, 1.1, startCenter);
   ScaleFromCenter(mySprite, o["scale"], o["scale"], startCenter);
  });

  myTween.addEventListener(TweenEvent.MOTION_FINISH, function(evt:TweenEvent):void {
   setTimeout(function():void {
    var tf:TextField = new TextField();
    tf.autoSize = TextFieldAutoSize.LEFT;
    tf.text = "Finished...";
    mySprite.addChild(tf);
   }, 100);
  });
 }

 private function ScaleRightAndDown(ob:*, sx:Number, sy:Number, ptScalePoint:Point):void {
  var m:Matrix = ob.transform.matrix;
  m.a = sx;
  m.d = sy;
  mySprite.transform.matrix = m;
 }

 private function ScaleFromCenterConstant(ob:*, sx:Number, sy:Number, ptScalePoint:Point):void {
  var m:Matrix = ob.transform.matrix;
  m.ty -= ptScalePoint.y;
  m.tx -= ptScalePoint.x;
  m.scale(sx, sy);
  m.tx += ptScalePoint.x;
  m.ty += ptScalePoint.y;
  ob.transform.matrix = m;
 }

 // This does not work
 private function ScaleFromCenter(ob:*, sx:Number, sy:Number, ptScalePoint:Point):void {
  var m:Matrix = ob.transform.matrix;
  m.ty -= ptScalePoint.y;
  m.tx -= ptScalePoint.x;
  m.scale(sx, sy);
  m.a = sx;
  m.d = sy;
  m.tx += ptScalePoint.x;
  m.ty += ptScalePoint.y;
  ob.transform.matrix = m;
 }

} 
}

UPDATE :

Yay, thanks to Niko Nyman, I got it working. Here is the final code for the interested few :

package {

import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;
import flash.display.*;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.*;
import flash.utils.setTimeout;

public class Scaling extends Sprite
{

 private var startWidth:int = 400;
 private var startHeight:int = 150;
 private var startLineWidth:int = 5;
 private var startEllipse:int = 15;
 private var startCenter:Point = new Point(400, 300);

 public var mySprite:Sprite = new Sprite();
 public var myTween:Tween;

 public function Scaling() {
  this.stage.scaleMode = StageScaleMode.NO_SCALE;
  this.stage.align = StageAlign.TOP_LEFT;

  mySprite.graphics.beginFill(0xF7F3DC);
  mySprite.graphics.lineStyle(startLineWidth, 0xD35F72);
  mySprite.graphics.drawRoundRect(-startWidth/2, -startHeight/2, startWidth, startHeight, startEllipse, startEllipse);
  mySprite.graphics.endFill();

  mySprite.x = startCenter.x;
  mySprite.y = startCenter.y;

  mySprite.scale9Grid = new Rectangle(-startWidth/2 + startEllipse/2, -startHeight/2 + startEllipse/2, startWidth - startEllipse, startHeight - startEllipse);

  mySprite.scaleX = 0;
  mySprite.scaleY = 0;

  addChild(mySprite);

  setTimeout(GoGoGadgetScaling, 1000);
 }

 private function GoGoGadgetScaling():void {

  var o:Object = new Object();
  o["scale"] = 1;

  myTween = new Tween(o, "scale", Back.easeOut, 0.1, 1, 0.5, true);

  myTween.addEventListener(TweenEvent.MOTION_CHANGE, function(evt:TweenEvent):void {
   mySprite.scaleX = o["scale"];
   mySprite.scaleY = o["scale"];
  });

  myTween.addEventListener(TweenEvent.MOTION_FINISH, function(evt:TweenEvent):void {
   setTimeout(function():void {
    var tf:TextField = new TextField();
    tf.autoSize = TextFieldAutoSize.LEFT;
    tf.text = "Finished...";
    mySprite.addChild(tf);
   }, 100);
  });
 }

} 
}
A: 

Have you tried to use easeOut?

myTween = new Tween(o, "scale", Elastic.easeOut, 1, 1.5, 0.75, true);
victor hugo
A: 

If you're looking to "outgrow" and come back, you want the Back.easeOut transition. Might I also suggest that you use the Flash Authoring tool to speed up the creation of the graphics as well? You could use the Rectangle Primitive tool to create a rounded rectangle, convert it to a MovieClip or Sprite and select 9-slice-scaling. If you adjust the slices so they start and end where the curve starts, it will only scale the center up and the corners will stay round.

Typeoneerror
The Back.easeOut transition was what I needed, also I've put the scale9Grid to work with good results, thanks! Unfortunately I'm still having a little difficulty with the scaling, I've updated the question accordingly.
Patrick J Collins
Looks like you're scaling from 1–5, which would be 500% larger. Could be why it's going off the screen. Pretty big increase.
Typeoneerror
Well, I'm starting off with a width of 50px, so a 500% increase would result in a final width of 250px, if my math is right. The problem with calling Matrix.scale() is that each subsequent call is exponential.
Patrick J Collins
A: 

Could you just draw the graphics centered (ie. put the top-left corner at x = -startWidth/2 and y = -startHeight/2 instead of 0, 0), then do a simple tween of scale on mySprite? This way you could get rid of all the matrix calculations...

Niko Nyman
Brilliant, thanks!
Patrick J Collins