JavaFX Abacus Tutorial, Part III
This is the third part of a basic tutorial on JavaFX. The other parts can be found here and the code is on github. The running example of the tutorial is to build an abacus. If you are curious how the final solution will look like, please
have look at this 7 min video.
Today, we will enrich the current state of the project with animation.
If you have done your homework – and I assume all of you have done so, right? -
then you should have a solution like in the
picture below where you can click on any ball and it will jump between its positions on the
left and right hand side.
For moving the ball, we used a Translation, which as we learned is one of many possible
Transformations.
Now, our current balls move in a rather weird fashion: they disappear from one location
to reappear in a different location. Until we have star-trek like teleportation,
this is an unrealistic behavior – and not very pleasing for the human eye.
JavaFX allows us to let the ball move in an animated fashion with a so-called Transition.
A Transition is an animation of a Transformation, in our case
the animation of a Translation.
Transitions have another interesting quality: they animate the transformation in
a “natural” way. A physical ball would never start with zero velocity, then suddenly
move at 10 km/h, and then stop instantly. It would accelerate and decelerate.
Transitions mimic this behavior by animating the underlying transformation
with an “ease-in” and “ease-out” by default.
Setting up a transition is easy since their are first-class objects in
JavaFX and can even be constructed with a builder:
|
1 2 3 4 5 |
TranslateTransition move = TranslateTransitionBuilder.create() .node(ball) .toX(newX) .duration(javafx.util.Duration.millis(200)) .build(); |
In order to execute the move do:
|
1 |
move.playFromStart(); |
With all that knowledge applied to our abacus implementation, the solution now becomes:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
package solution; import javafx.animation.TranslateTransition; import javafx.animation.TranslateTransitionBuilder; import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.shape.Circle; import javafx.scene.shape.CircleBuilder; import javafx.stage.Stage; import static javafx.util.Duration.millis; public class Abacus_3_Ball_transition extends Application { private static final int ROW_COUNT = 10; private static final int COL_COUNT = 10; private static final int RADIUS = 20; private static final int DIAMETER = 2 * RADIUS; private static final int MOVE_WAY = 8 * DIAMETER; private static final int WIDTH = COL_COUNT * DIAMETER + MOVE_WAY; private static final int HEIGHT = ROW_COUNT * DIAMETER; private static final int PADDING = 20; private static final int OFFSET = PADDING + RADIUS; @Override public void start(Stage primaryStage) { primaryStage.setTitle("JavaFX Abacus"); Pane root = new Pane(); for (int row = 0; row < ROW_COUNT; row++) { for (int column = 0; column < COL_COUNT; column++) { final Circle circle = makeCircle(OFFSET + (row * DIAMETER), OFFSET + (column * DIAMETER)); root.getChildren().add(circle); } } primaryStage.setScene(new Scene(root, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING)); primaryStage.show(); } private Circle makeCircle(int y, int x) { final Circle ball = CircleBuilder.create().radius(RADIUS-1).centerX(x).centerY(y).build(); ball.setOnMouseClicked(new EventHandler<MouseEvent>() { public void handle(MouseEvent mouseEvent) { double newX = MOVE_WAY; if (ball.getTranslateX() > 1) newX = 0; TranslateTransition move = TranslateTransitionBuilder.create() .node(ball) .toX(newX) .duration(millis(200)) .build(); move.playFromStart(); } }); return ball; } public static void main(String[] args) { launch(args); } } |
Homework
Your homework assignment until next week is to also paint some rails where the balls can sit on.
Next week we will start looking into JavaFX property binding.
Have fun coding until then
Dierk König
P.S. the GroovyFX version by Tim Yates: here.













