Somewhere in someone blog, I read this “Complex thing should be doable and simple things should be simple” - this is what the power of a Language.
Many of us have seen lot of Samples in JavaFX and my favorites are those in which complex things are done quite easy, like PhotoFlip. http://www.javafx.com/samples/PhotoFlip/index.html. You can see how complex calculation goes for a perspective transform.
Using it in a simpler form, I tried to write Cascade transformed Frames, which looks something like this :

You can play with 2 buttons. Sorry for not making some flashy button, I simple used Swing Buttons.
By code is little buggy, so bear with it.
- Moving Mouse on any frame, will make it front.
- Close button will close that frame. (It is only possible in non-perspective mode).
- Top bar can be useful for dragging the frames(again good at non-perspective mode, in perspective mode, use the left most corner to drag it, you can figure out why is so ?
).
- Text will be as clear as it was in original mode.
- Sharing common reason in case of toFront() make the effect little flckry.
(One problem solved, thanks for José Miguel in comment section - Code changed)
Its all in around 100-150 lines of code. Feel free for suggestions. This can be used for multi-frame work like showing Car models, parts of engine.
Code :
Main.fx
package cascade;
import cascade.Frame;
import javafx.ext.swing.SwingButton;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
* @author Vaibhav Choudhary
*/
var bt: Frame = Frame{ x: 20, y: 140 };
var bt1: Frame = Frame{ x: 100, y: 220};
var bt2: Frame = Frame{ x: 180, y: 300};
var gp = Group {
};
insert bt into gp.content;
insert bt1 into gp.content;
insert bt2 into gp.content;
Stage {
title: "Application title"
width: 550
height: 580
scene: Scene {
fill: Color.GRAY
content: [
gp
SwingButton {
translateX: 10
translateY: 10
text: "Transform"
action: function() {
bt1.t.playFromStart();
bt.t.playFromStart();
bt2.t.playFromStart();
}
}
SwingButton {
translateX: 100
translateY: 10
text: "Normal"
action: function() {
bt1.t.rate = -1; bt1.t.play();
bt2.t.rate = -1; bt2.t.play();
bt.t.rate = -1; bt.t.play();
}
}
]
}
}
Frame.fx
package cascade;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.CustomNode;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.Group;
import javafx.scene.input.MouseEvent;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
/**
* @author Vaibhav Choudhary
*/
public class Frame extends CustomNode {
public var startx: Number;
public var starty: Number;
public var x: Number;
public var y: Number;
var distX: Number;
var distY: Number ;
public var clip_ = 0.0;
public var t = Timeline {
repeatCount: 1
keyFrames: [
KeyFrame {
time: 1s
canSkip: true
values: [
clip_ => -150.0 tween Interpolator.LINEAR
]
}
]
}
public var t_rev = Timeline {
repeatCount: 1
keyFrames: [
KeyFrame {
time: 1s
canSkip: true
values: [
clip_ => 0.0 tween Interpolator.LINEAR
]
}
]
}
public override function create(): Node {
return Group {
effect: PerspectiveTransform {
ulx: 0
uly: 0
urx: 300
ury: bind clip_
lrx: 300
lry: bind clip_ + 150
llx: 0
lly: 150
}
cache: true
translateX: bind x + startx
translateY: bind y + starty
content: [
Rectangle {
x: 0,
y: 0
opacity: 0.6
width: 300
height: 150
fill: Color.BLACK
onMouseMoved: function( e: MouseEvent ):Void {
this.toFront();
}
},
Text {
fill: Color.WHITE
font: Font {
size: 14
name: "Arial Bold"
}
x: 10,
y: 40
content: "I am living on a 3D Frame. You can n transform me using the Transform n Button at the top, "
"you can set n me normal using normal button "
}
Rectangle {
x: 1,
y: 1
width: 299,
height: 20
opacity: 0.8
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
stops: [
Stop {
color: Color.GRAY
offset: 0.0
},
Stop {
color: Color.BLACK
offset: 1.0
},
]
}
onMousePressed: function (e:MouseEvent) : Void{
distX = startx;
distY = starty;
}
onMouseDragged: function( e: MouseEvent ):Void {
startx =distX + e.dragX;
starty =distY + e.dragY;
}
},
Rectangle {
x: 280,
y: 3
width: 15,
height: 15
opacity: 0.7
onMouseClicked: function( e: MouseEvent ):Void {
this.visible = false;
}
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
stops: [
Stop {
color: Color.ORANGE
offset: 0.0
},
Stop {
color: Color.DARKRED
offset: 0.5
},
Stop {
color: Color.ORANGE
offset: 1.0
},
]
}
arcHeight: 5
arcWidth: 5
},
Text {
font: Font {
size: 20
}
x: 283,
y: 17
content: “x”
}
]
};
}
}
View the JNLP here

If liked it, then why not share!









