Using Perspective Transform for a 3-D Flip Transition

By Josh Marinacci, October 17th, 2008

You can easily create 3-D transitions in JavaFX by using the PerspectiveTransform effect.

Understanding the Code

All JavaFX graphics nodes have an effect variable, which you can use to apply special effects to that node. The PerspectiveTransform effect stretches the node to fit a four-sided polygon defined by four corner points. By carefully calculating these points you can create a 3-D flipping effect.

The code in Figure 1 defines a custom node called FlipView. It has two public variables for the front and back nodes and a third internal variable called time. The create function returns a Group containing two other Groups, one for the front and back nodes. The important part here is that the effect of each group is bound to the getPT function, which takes the time variable. Because it is bound, the getPT function is called every time the time variable changes. It is in the getPT function where the PerspectiveTransform is actually calculated based on the current time.

Source Code
package fliptransition;

import javafx.scene.effect.*;
...

class FlipView extends CustomNode {
    public var frontNode:Node;
    public var backNode:Node;

    var flipped = false;
    var time = Math.PI/2;
    override public function create():Node {
        return Group {
            content: [ 
                Group { content: backNode visible: bind (time<0) effect: bind getPT(time) },
                Group { content: frontNode  visible: bind (time>0) effect: bind getPT(time) },
            ]
        }
    }

Figure 1: FlipView class

The getPT function uses the Math.sin() and Math.cos() methods to stretch the dimensions of the node to look like it is at a 3-D angle. On each flip, the side of the rectangle that rotates toward the back is made slightly smaller. The side of the rectangle that rotates toward the front is made slightly taller. These rotations are what creates the illusion of a 3-D transition. The width and height constants match the size of the front and back nodes. The radius and back variables can be adjusted to customize the exact angle and distortion of the effect.

Source Code
    function getPT(t:Number):PerspectiveTransform {
        var width = 200;
        var height = 200;
        var radius = width/2;
        var back = height/10;
        return PerspectiveTransform {
            ulx: radius - Math.sin(t)*radius     uly: 0 - Math.cos(t)*back
            urx: radius + Math.sin(t)*radius     ury: 0 + Math.cos(t)*back
            lrx: radius + Math.sin(t)*radius     lry: height - Math.cos(t)*back
            llx: radius - Math.sin(t)*radius     lly: height + Math.cos(t)*back
        }
    }

Figure 2: getPT Function

To actually animate the transition, the anim timeline moves the time variable from negative 3.14/2 (or 90 degrees in radians) to -3.14/2 over a single second. For details see Figure 3.

public var anim = Timeline { keyFrames: [ at(0s) { time=> Math.PI/2 tween Interpolator.LINEAR}, at(1s) { time=> -Math.PI/2 tween Interpolator.LINEAR}, KeyFrame { time: 1.0s action: function() { flipped = not flipped; } } ] }

Figure 3: Animate the Transition

To use the FlipView you just create it and pass in content nodes for the front and back, as Figure 4 shows.

Source Code
var flip = FlipView {
    translateX: 50
    translateY: 40+50
    backNode: ImageView { image: Image { url: "{__DIR__}lion1.png"  }  }
    frontNode: ImageView { image: Image { url: "{__DIR__}lion2.png"  }  }
};

Figure 4: Create a FlipView