Spring Animation Effects With a Custom Interpolator
JavaFX provides default interpolators for tween animation, but you can also create your own for some cool effects. This example shows how to create a spring interpolator.
Understanding the Code
By default an animation is linear, meaning the object being animated moves at a constant speed. An "ease-in" animation means the objects starts moving slowly and then speeds up. "Ease-out" is the reverse, with an object starting at normal speed then slowing down when it nears the end. This behavior is controlled by an Interpolator object. JavaFX has default interpolators for LINEAR, EASEIN, EASEOUT, and ease-in plus ease-out (EASEBOTH). However, sometimes you want a different kind of interpolation. JavaFX enables you to create your own custom interpolators by subclassing Interpolator or SimpleInterpolator. This example shows you how to create a springlike motion by using a custom interpolator.
The interpolator itself subclasses SimpleInterpolator and implements a single function: curve. This is the curve of t (time) over the length of the animation. By returning different values of t as it goes from 0.0 to 1.0 (representing the duration of the animation) you can create complex interpolation behavior. To create a springlike animation the code in Figure 1 creates the SpringInterpolator using the standard physics spring equation.
import javafx.animation.SimpleInterpolator;
import java.lang.Math;
public class SpringInterpolator extends SimpleInterpolator {
// the amplitude of the wave
// controls how far out the object can go from it's final stopping point.
public-init var amplitude:Number = 1.0;
// determines the weight of the object
// makes the wave motion go longer and farther
public-init var mass:Number = 0.058;
// the stiffness of the wave motion / spring
// makes the motion shorter and more snappy
public-init var stiffness:Number = 12.0;
// makes the wave motion be out of phase, so that the object
// doesn't end up on the final resting spot.
// this variable is usually never changed
public-init var phase:Number = 0.0;
// if this should do a normal spring or a bounce motion
public-init var bounce:Boolean = false;
// internal variables used for calcuations
var pulsation:Number;
init {
this.pulsation = Math.sqrt(stiffness / mass);
}
// the actual spring equation
override public function curve(t: Number) : Number {
var t2 = -Math.cos(pulsation*t+phase+Math.PI) * (1-t) * amplitude ;
// use the absolute value of the distance if doing a bounces
if(bounce) {
return 1-Math.abs(t2);
} else {
return 1-t2;
}
}
}
Figure 1: SpringInterpolator.fx Code
To use a custom interpolator, you must create an instance of it, then use it in your keyframes with the tween keyword. The code in Figure 2 creates a new SpringInterpolator then defines a variable, springAnimY. The springAnim timeline animates the springAnimY variable from 0 to 200 over 1.5 seconds. The tween spring makes the animation use the spring interpolator instead of a standard interpolator. Finally the group containing a ball image is bound to the springAnimY variable, making it move with the timeline.
def spring = SpringInterpolator { bounce: false};
var springAnimY = 50;
var springAnim = Timeline {
keyFrames: [ at(1.5s) { springAnimY => 200 tween spring}, ]
};
// bind the to the springAnimY variable which will animate it
var springImage = ImageView {
translateX: 20
translateY: bind springAnimY;
image: Image { url: "{__DIR__}image/ball.png" }
onMousePressed:function(e:MouseEvent) {
springAnim.time = 0s;
springAnim.play();
}
};
Figure 2: SpringTest.fx Code
This example actually has two animations, one for spring and one for bouncing. The bouncing version is exactly the same equation except that it uses the absolute value of t to reflect the spring waves. (See the end of Figure 1) As a result the object appears to bounce off of its endpoint rather than going past it and coming back. To use this behavior, set the bounce variable of the SpringInterpolator to true.
Josh MarinacciStaff Engineer,
Sun Microsystems
