In this project, I took an image that was in the shape of a ball and gave it a bouncy ball animation. This bouncy ball animation includes a stretch animation during flight and squash animation when it makes contact with the ground. I also gave it a horizon and a reflection just for kicks.
To start off, I created a normal Flex project using the Flex Builder 3 Plugin for Eclipse. My project is named BouncyBall, so naturally my main mxml file is BouncyBall.mxml. In addition, I have a globe image and a couple of components. I created the BallPanel.mxml file to hold the bouncy ball. To create the reflection, I used the actionscript based component, Reflector, from this LiveReflector Project. I also created a gradient that would serve as my horizon or floor. This is how things started to shape up.
BouncyBall.mxml (skeleton)
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:comp="components.*" width="300" height="350"
backgroundColor="0xffffff" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#FFFFFF, #FFFFFF]"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
creationComplete="init()">
<mx:Script>
<![CDATA[
private function init():void {
}
]]>
</mx:Script>
<mx:UIComponent id="floorWrapper" x="0" y="280" height="70" width="100%" />
<comp:Reflector id="myReflector" target="{ballPanel}" y="310" alpha="0.4" falloff="0.2" blurAmount="0.2" width="100%" />
<comp:BallPanel id="ballPanel" width="100%" height="300" left="0" ballSquashScale="1.75" />
</mx:Application>BallPanel.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable] public var ballX:Number;
[Bindable] public var ballSquashScale:Number = 2;
[Bindable] public var ballSquashX:Number;
private function init():void {
ballX = (width - ball.width) / 2;
ballSquashX = ballX - ((ball.width * ballSquashScale - ball.width) / 2);
ball.x = ballX;
}
public function get ballSquashScaleX():Number {
return ballSquashScale;
}
public function get ballSquashScaleY():Number {
return 1 / ballSquashScale;
}
]]>
</mx:Script>
<mx:Image id="ball" source="assets/globe.png" x="0" y="0" height="49" width="50" />
</mx:Canvas>The only thing left is the bounce sequence that I created that makes everything work together. There are 4 sets of animations that need to take place: the drop(strech), squash, unsquash, and bounce up(unstretch).
BouncyBall.mxml > bounce:Sequence
<mx:Sequence id="bounce" target="{ballPanel.ball}" duration="{1000 * durationFactor}" repeatCount="0">
<mx:Parallel>
<mx:AnimateProperty property="y"
fromValue="0"
toValue="{ballPanel.height - ballPanel.ball.height * (1 + .2 * squishyFactor)}"
easingFunction="{Cubic.easeIn}"/>
<mx:AnimateProperty property="scaleY"
fromValue="1"
toValue="{1 + .2 * squishyFactor}"
easingFunction="{Sine.easeIn}"/>
</mx:Parallel>
<mx:Parallel duration="{200 * durationFactor}">
<mx:AnimateProperty property="y"
fromValue="{ballPanel.height - ballPanel.ball.height * (1 + .2 * squishyFactor)}"
toValue="{ballPanel.height - ballPanel.ball.height * ballPanel.ballSquashScaleY}" easingFunction="{Cubic.easeOut}"/>
<mx:AnimateProperty property="scaleY"
fromValue="{1 + .2 * squishyFactor}"
toValue="{ballPanel.ballSquashScaleY}"
easingFunction="{Cubic.easeOut}"/>
<mx:AnimateProperty property="scaleX"
toValue="{ballPanel.ballSquashScaleX}"
easingFunction="{Cubic.easeOut}"/>
<mx:AnimateProperty property="x"
toValue="{ballPanel.ballSquashX}"
easingFunction="{Cubic.easeOut}"/>
</mx:Parallel>
<mx:Parallel duration="{200 * durationFactor}">
<mx:AnimateProperty property="y"
fromValue="{ballPanel.height - ballPanel.ball.height * ballPanel.ballSquashScaleY}"
toValue="{ballPanel.height - ballPanel.ball.height * (1 + .2 * squishyFactor)}" easingFunction="{Cubic.easeIn}"/>
<mx:AnimateProperty property="scaleY"
toValue="{1 + .2 * squishyFactor}"
easingFunction="{Cubic.easeIn}"/>
<mx:AnimateProperty property="scaleX"
toValue="1"
easingFunction="{Cubic.easeIn}"/>
<mx:AnimateProperty property="x"
toValue="{ballPanel.ballX}"
easingFunction="{Cubic.easeIn}"/>
</mx:Parallel>
<mx:Parallel>
<mx:AnimateProperty property="y"
toValue="0" fromValue="{ballPanel.height - ballPanel.ball.height * (1 + .2 * squishyFactor)}"
easingFunction="{Cubic.easeOut}"/>
<mx:AnimateProperty property="scaleY"
toValue="1"
easingFunction="{Sine.easeOut}"/>
</mx:Parallel>
</mx:Sequence>I also need to write the necessary actionscript to jumpstart this process. In the earlier code, I am calling the init function upon creationComplete of the application tag. Below is the init function, import statements, and variable definitions needed.
BouncyBall.mxml > Script
import mx.effects.easing.Sine;
import mx.effects.easing.Cubic;
private var floor:Sprite;
[Bindable] private var durationFactor:Number = 1;
[Bindable] private var squishyFactor:Number = 2;
private function init():void {
floor = new Sprite();
var colors:Array = [0xffffff, 0xdddddd];
var ratios:Array = [0, 255];
var matrix:Matrix = new Matrix();
matrix.createGradientBox(300, 50, -Math.PI / 2);
floor.graphics.beginGradientFill(GradientType.LINEAR, colors, null, ratios, matrix);
floor.graphics.drawRect(0, 0, 300, 50);
floorWrapper.addChild(floor);
bounce.play();
}Here is the resulting swf.
I was inspired to begin working with this bouncy ball animation by reading several ball animation articles at Codedependent. Specifically, I enjoyed Stretch & Squash for Flex 3. First, I switched the logic from using a gradient ball to an image of my choice. The switch meant going from skewing height and width values to skewing scaleX and scaleY values which was a whole new ball game. I hope somebody gets some use out of my tireless hours in front of my laptop.
Here is the Flex Project.
[Download not found]
over my head jon, way over…..
Thanks for the comment though
Thanks for the comment though
Hey Jon,
Very cool effect!
I need to create an object that rises slightly with a mouseOver, then settles back to it's original position. Not nearly as complicated as a bounce. I was using your code as a guide to managing the live reflection. However, my reflection effect takes a moment to update after the object it's reflecting has already completed its move. Any idea why that happens?
In any event, thanks for sharing your animation. I've learned alot from it.
I'm not that familiar with the inner workings of the live reflector project. I didn't have any problems getting the reflection to work in my project. Make sure that there is no overlapping (or at least be aware of it) of the target object and the reflector object, which could cause some display problems.