Jeet Parekh
Jeet Parekh

Reputation: 740

Need help in PathTransition in JavaFX

I am trying to get around 8-12 circles rotate in a circle behind each other.

Here is the code I am trying

import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Cirlces extends Application {

    @Override
    public void start(final Stage stage) throws Exception {
        final Group group = new Group();
        final Scene scene = new Scene(group, 500, 500, Color.WHITE);
        stage.setScene(scene);
        stage.setTitle("Circles");
        stage.show();
        final Circle circle = new Circle(20, 20, 15);
        circle.setFill(Color.DARKRED);

        Circle path = new Circle(250,250,200);
        path.setFill(Color.WHITE);

        group.getChildren().add(path);
        group.getChildren().add(circle);
        final PathTransition pathTransition = new PathTransition();

        pathTransition.setDuration(Duration.seconds(2.0));
        pathTransition.setDelay(Duration.ZERO);
        pathTransition.setPath(path);
        pathTransition.setNode(circle);
        pathTransition
                .setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
        pathTransition.setCycleCount(Timeline.INDEFINITE);
        //pathTransition.setAutoReverse(true);
        pathTransition.play();
    }

    public static void main(final String[] arguments) {
        Application.launch(arguments);
    }
}

On compiling and executing this code, I get a circle which goes on a circular path. But after each rotation, there is a slight delay before the next rotation starts, even though I have set the delay to zero.

So using this code, when I create multiple circles on the path (I take a different path for each circle, so it looks like they are on the same path and start each individual from a later time phase using playFrom() to synchronize their position), it gets really messy.

What I want is, multiple circles moving on a circular path uniformly, but that slight delay makes it look very bad.

So my questions are

  1. What can I do to remove that delay? Would constructing the path in a different way help? Is there any way to remove that delay?

  2. The rotation of the circle starts from 3'o clock position on the path, is there any way to change the initial position of the node on the path?

  3. Can I add multiple nodes to the same path? Say 5 circle nodes to the path, with different initial positions.

Upvotes: 1

Views: 2170

Answers (2)

jewelsea
jewelsea

Reputation: 159416

You can use multiple path transitions (one per node), with the starting time of each transition offset by the distance along the path that you want a node to start at (using jumpTo).

This is a (simplified) variation on the solution to: "How to write text along a Bezier Curve?". The solution will work for nodes traveling along an arbitrary path, not just nodes traveling in a circle.

path transition

import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;

public class CirclePathTransitions extends Application {
    private static final int NUM_NODES = 5;
    private static final double W = 200;
    private static final double H = 200;
    private static final double NODE_SIZE = H / 8.0;

    @Override
    public void start(final Stage stage) throws Exception {
        Pane content = new Pane();
        content.setMinSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
        content.setPrefSize(W, H);
        content.setMaxSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
        content.setStyle("-fx-background-color: coral;");

        Shape path = new Circle(W/2, H/2, H * 3 / 8.0 - NODE_SIZE);

        content.getChildren().add(new Circle(W/2, H/2, H * 3 / 8.0 - NODE_SIZE, Color.PALEGREEN));

        for (int i = 0; i < NUM_NODES; i++) {
            Node node = new Circle(NODE_SIZE / 2, Color.MIDNIGHTBLUE);
            content.getChildren().add(node);

            final Transition transition = createPathTransition(path, node);
            transition.jumpTo(Duration.seconds(10).multiply(i * 1.0 / NUM_NODES));
            transition.play();
        }

        stage.setScene(new Scene(new StackPane(content), W, H, Color.ALICEBLUE));
        stage.show();
    }

    private PathTransition createPathTransition(Shape shape, Node node) {
        final PathTransition transition = new PathTransition(
                Duration.seconds(10),
                shape,
                node
        );

        transition.setAutoReverse(false);
        transition.setCycleCount(PathTransition.INDEFINITE);
        transition.setInterpolator(Interpolator.LINEAR);

        return transition;
    }

    public static void main(String[] args) throws Exception {
        launch(args);
    }
}

Upvotes: 3

James_D
James_D

Reputation: 209418

  1. Call pathTransition.setInterpolator(Interpolator.LINEAR);
  2. You can rotate the path: path.setRotate(-90);
  3. I don't think there's an easy way to do this (with different starting positions), still using the PathTransition. You can achieve a similar effect using a Timeline and binding a rotation for each circle to a property you "animate" with the timeline:

-

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;

public class CircleTransitions extends Application {

    @Override
    public void start(final Stage stage) throws Exception {
        final Group group = new Group();
        final Scene scene = new Scene(group, 500, 500, Color.WHITE);
        stage.setScene(scene);
        stage.setTitle("Circles");
        stage.show();

        Circle path = new Circle(250,250,200);
        path.setFill(Color.WHITE);

        DoubleProperty angle = new SimpleDoubleProperty();
        Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(2), new KeyValue(angle, 360)));
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.play();

        group.getChildren().add(path);

        for (int i=0; i<5; i++) {
            Circle circle = new Circle(250, 450, 15);
            circle.setFill(Color.DARKRED);
            Rotate rotate = new Rotate(0, 250, 250);
            circle.getTransforms().add(rotate);
            rotate.angleProperty().bind(angle.add(360.0 * i / 5));
            group.getChildren().add(circle);
        }
    }

    public static void main(final String[] arguments) {
        Application.launch(arguments);
    }
}

Upvotes: 4

Related Questions