Skip to content

Instantly share code, notes, and snippets.

@corytodd
Created June 13, 2018 02:58
Show Gist options
  • Save corytodd/ff290b2897672f660cb3e8f27894a105 to your computer and use it in GitHub Desktop.
Save corytodd/ff290b2897672f660cb3e8f27894a105 to your computer and use it in GitHub Desktop.
JavaFX two-sided image widget
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.geometry.Point3D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
/**
* Two-sided image pane is a class that takes two images and creates a
* composite Node with a front and back image. The rear image is properly
* transformed so as to prevent mirroring. Rotation and flip methods
* are available. Flip will rotate the object 180 degrees along the X-axis
* while rotate will rotate the current face 180 degrees about the Z-axis.
*
*
* Created by cory on 6/9/2015.
*/
public class TwoSidedImagePane extends StackPane {
private final Timeline flipUp, flipDown, rotateLeft, rotateRight;
// These track our current position so we don't have to make calls to get our X,Y,Z coordinates.
private boolean isDown = false;
private boolean isRight = false;
private final double animationSpeed;
private ImageView back;
private ImageView front;
/**
* Creates a new two-sided pane. This behaves like a stack pane so remember to set the
* sizing parameters in this node's parent accordingly.
* @param imageFront Image to be visible when there is 0 degrees rotation on the X-axis
* @param imageBack Image to be visible when there is 180 degrees rotation on the X-axis
* @param speed Animation time in milliseconds. This is the duration of one flip or rotation.
* @return TwoSidedImage
*/
public static TwoSidedImagePane createInstance(Image imageFront, Image imageBack, double speed) {
return new TwoSidedImagePane(imageFront, imageBack, speed);
}
private TwoSidedImagePane(Image imageFront, Image imageBack, double speed) {
animationSpeed = speed;
back = new ImageView(imageBack);
// normal (Z) rotation axis
back.setRotationAxis(new Point3D(0, 0, 1));
front = new ImageView(imageFront);
// normal (Z) rotation axis
front.setRotationAxis(new Point3D(0, 0, 1));
// Back is mirrored so we use a transform to correct this
Rotate backRot = new Rotate(180, Rotate.Y_AXIS);
backRot.setPivotX(back.prefWidth(USE_PREF_SIZE)/2);
back.getTransforms().add(backRot);
this.getChildren().addAll(back, front);
// normal (x) flipping axis
this.setRotationAxis(new Point3D(1, 0, 0));
// From up position, (1)rotate along the current axis, (2) push this image down below back image, (3)finish rotation
// 0 degrees is fully up, 180 degrees is fully down
flipDown = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(this.rotateProperty(), 0d)),
new KeyFrame(
Duration.millis(animationSpeed / 2),
t -> {
// Effectively lowers the Z-index of front
front.toBack();
},
new KeyValue(this.rotateProperty(), 90d)),
new KeyFrame(
Duration.millis(animationSpeed),
new KeyValue(this.rotateProperty(), 180d)));
// From down position, (1) rotate along the current axis, (2) pull front image to top, (2) finish rotation
// 0 degrees is fully up, 180 degrees is fully down
flipUp = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(this.rotateProperty(), 180d)),
new KeyFrame(
Duration.millis(animationSpeed / 2),
t -> {
// Effectively raises the Z-index of front
front.toFront();
},
new KeyValue(this.rotateProperty(), 90d)),
new KeyFrame(
Duration.millis(animationSpeed),
new KeyValue(this.rotateProperty(), 0d)));
// Dually rotate front and back images along their Z-axis. 0 is fully left, 180 is fully right.
rotateRight = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(back.rotateProperty(), 0d)),
new KeyFrame(
Duration.ZERO,
new KeyValue(front.rotateProperty(), 0d)),
new KeyFrame(
Duration.millis(animationSpeed),
new KeyValue(back.rotateProperty(), 180d)),
new KeyFrame(
Duration.millis(animationSpeed),
new KeyValue(front.rotateProperty(), 180d)));
// Dually rotate front and back images along their Z-axis. 0 is fully left, 180 is fully right.
rotateLeft = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(back.rotateProperty(), 180d)),
new KeyFrame(
Duration.ZERO,
new KeyValue(front.rotateProperty(), 180d)),
new KeyFrame(
Duration.millis(animationSpeed),
new KeyValue(back.rotateProperty(), 0d)),
new KeyFrame(
Duration.millis(animationSpeed),
new KeyValue(front.rotateProperty(), 0d)));
// We don't want to rollback the movement after every animation, keep the translations that we make.
flipUp.setAutoReverse(false);
flipDown.setAutoReverse(false);
rotateLeft.setAutoReverse(false);
rotateRight.setAutoReverse(false);
}
/**
* Makes visible the opposite side of this object.
*/
public synchronized void flip() {
// X-axis goes from left to right parallel to the monitor screen
if (isDown) {
flipUp.play();
}
else {
flipDown.play();
}
isDown = !isDown;
}
/**
* Rotates the current face by 180 degrees
*/
public synchronized void rotate() {
// Z-axis is the axis showing depth. This line can be visualized as the axis
// from you to the monitor (pretend you have proper posture and this is a straight line)
if(isRight)
rotateLeft.play();
else
rotateRight.play();
isRight = !isRight;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment