Javafx: 如何使用3D路径制作动画?

4
我对JavaFX很陌生,在尝试动画处理时遇到一个问题。 我知道类PathTransition提供了一些方法来沿着任意曲线通过类Path在两个点之间移动节点;但是似乎与PathTransition相关的所有类,如PathMoveToCubicCurveTo甚至包括它本身,都只能在xy平面上工作。如果我想在yz平面或xz平面上移动节点怎么办?我在互联网上找不到任何信息。感谢提供任何建议。
1个回答

7
动画基础Animations所示,您可以组合多种类型的Transition,包括PathTransition,在SequentialTransitionParallelTransition中。当运动方程可以用参数形式表示时,这种方法特别方便。下面展示了沿着螺旋线移动的动画,使用ParallelTransitionCircle上的PathTransition与直线上的Timeline结合起来。
animation = new ParallelTransition(
    createTransition(circle, arrow),
    createTimeline(size / 2));

image

import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.ParallelTransition;
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.Timeline;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.effect.Bloom;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @see https://dev59.com/lufys4cB2Jgan1zniVxa#37370840
 */
public class Helix extends Application {

    private static final double SIZE = 300;
    private final Content content = Content.create(SIZE);

    public void play() {
        content.animation.play();
    }

    private static final class Content {

        private static final Duration DURATION = Duration.seconds(4);
        private static final Color COLOR = Color.AQUA;
        private static final double WIDTH = 3;
        private final Group group = new Group();
        private final Rotate rx = new Rotate(0, Rotate.X_AXIS);
        private final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
        private final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
        private final Box xAxis;
        private final Box yAxis;
        private final Box zAxis;
        private final Shape circle;
        private final Shape arrow;
        private final Animation animation;

        private static Content create(double size) {
            Content c = new Content(size);
            c.group.getChildren().addAll(c.arrow, c.circle,
                c.xAxis, c.yAxis, c.zAxis);
            c.group.getTransforms().addAll(c.rz, c.ry, c.rx);
            c.group.setTranslateX(-size / 2);
            c.group.setTranslateY(-size / 2);
            c.group.setTranslateZ(size / 2);
            c.rx.setAngle(35);
            c.ry.setAngle(-45);
            return c;
        }

        private Content(double size) {
            xAxis = createBox(size, WIDTH, WIDTH);
            yAxis = createBox(WIDTH, size, WIDTH);
            zAxis = createBox(WIDTH, WIDTH, size);
            circle = createCircle(size);
            arrow = createShape();
            animation = new ParallelTransition(
                createTransition(circle, arrow),
                createTimeline(size / 2));
        }

        private Circle createCircle(double size) {
            Circle c = new Circle(size / 4);
            c.setFill(Color.TRANSPARENT);
            c.setStroke(COLOR);
            return c;
        }

        private Box createBox(double w, double h, double d) {
            Box b = new Box(w, h, d);
            b.setMaterial(new PhongMaterial(COLOR));
            return b;
        }

        private Shape createShape() {
            Shape s = new Polygon(0, 0, -10, -10, 10, 0, -10, 10);
            s.setStrokeWidth(WIDTH);
            s.setStrokeLineCap(StrokeLineCap.ROUND);
            s.setStroke(COLOR);
            s.setEffect(new Bloom());
            return s;
        }

        private Transition createTransition(Shape path, Shape node) {
            PathTransition t = new PathTransition(DURATION, path, node);
            t.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
            t.setCycleCount(Timeline.INDEFINITE);
            t.setInterpolator(Interpolator.LINEAR);
            return t;
        }

        private Timeline createTimeline(double size) {
            Timeline t = new Timeline();
            t.setCycleCount(Timeline.INDEFINITE);
            t.setAutoReverse(true);
            KeyValue keyX = new KeyValue(group.translateXProperty(), size);
            KeyValue keyY = new KeyValue(group.translateYProperty(), size);
            KeyValue keyZ = new KeyValue(group.translateZProperty(), -size);
            KeyFrame keyFrame = new KeyFrame(DURATION.divide(2), keyX, keyY, keyZ);
            t.getKeyFrames().add(keyFrame);
            return t;
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("JavaFX 3D");
        Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
        primaryStage.setScene(scene);
        scene.setFill(Color.BLACK);
        scene.setOnMouseMoved((final MouseEvent e) -> {
            content.rx.setAngle(e.getSceneY() * 360 / scene.getHeight());
            content.ry.setAngle(e.getSceneX() * 360 / scene.getWidth());
        });
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setFarClip(SIZE * 6);
        camera.setTranslateZ(-3 * SIZE);
        scene.setCamera(camera);
        scene.setOnScroll((final ScrollEvent e) -> {
            camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
        });
        primaryStage.show();
        play();
    }

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

在这个相关的示例中,黄色形状遵循一个由立方体正交轴旋转组成的Timeline动画,并沿着立方体面的边缘遵循PathTransition

image

cube.setOnMouseMoved(new EventHandler<MouseEvent>() {
    @Override
    public void handle(final MouseEvent e) {
        animation = new Timeline();
        animation.getKeyFrames().addAll(
            new KeyFrame(new Duration(2000),
                new KeyValue(cube.rx.angleProperty(), e.getY()),
                new KeyValue(cube.ry.angleProperty(), -e.getX()),
                new KeyValue(cube.rz.angleProperty(), e.getY())
            ));
        animation.play();
    }
});
…
pathBackFaceTransition = new PathTransition();
pathBackFaceTransition.setPath(rectangleBackFace);
…
pathFrontFaceTransition = new PathTransition();
pathFrontFaceTransition.setPath(rectangleFrontFace);
…
public void play() {
    pathBackFaceTransition.play();
    pathFrontFaceTransition.play();
}

最后,你可以通过使用针对比例尺和平移属性的KeyValue来模拟沿x、yz轴的运动。再次参考动画基础知识,对TimelineEvents进行以下更改,可以创建一个三维形状来回移动的幻觉。

image

//create a keyValue with factory: scaling the circle 2times
KeyValue keyValueX = new KeyValue(stack.scaleXProperty(), 2);
KeyValue keyValueY = new KeyValue(stack.scaleYProperty(), 2);
KeyValue keyTransX = new KeyValue(stack.translateXProperty(), 100);
KeyValue keyTransY = new KeyValue(stack.translateYProperty(), 100);

//create a keyFrame, the keyValue is reached at time 2s
Duration duration = Duration.millis(2000);
//one can add a specific action when the keyframe is reached
EventHandler<ActionEvent> onFinished = new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent t) {
        //reset counter
        i = 0;
    }
};
KeyFrame keyFrame = new KeyFrame(duration, onFinished,
    keyValueX, keyValueY, keyTransX, keyTransY);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接