Created
June 13, 2018 02:58
-
-
Save corytodd/ff290b2897672f660cb3e8f27894a105 to your computer and use it in GitHub Desktop.
JavaFX two-sided image widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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