Javafx:如何制作具有 3D 路径的动画?

王晟凱*_*王晟凱 4 animation javafx

我是 JavaFX 新手,在尝试处理动画时遇到了问题。我知道类PathTransition提供了按类沿任意曲线在两点之间移动节点的方法Path;但似乎所有与 相关的类PathTransition,例如PathMoveTo以及CubicCurveTo和 ,包括它本身,都只能在xy平面上工作。如果我想在yz平面或xz平面中移动节点怎么办?我只是在互联网上找不到任何有关它的信息。任何意见,将不胜感激。

tra*_*god 8

如动画基础知识中所示,Animations您可以在 a或中组合多种Transition,包括、 。当运动方程可以用参数形式表示时,该方法尤其方便。沿螺旋线的运动(如下所示)使用 a将 a沿 a与 a沿直线组合。PathTransitionSequentialTransitionParallelTransitionParallelTransitionPathTransitionCircleTimeline

\n\n
animation = new ParallelTransition(\n    createTransition(circle, arrow),\n    createTimeline(size / 2));\n
Run Code Online (Sandbox Code Playgroud)\n\n

图像

\n\n
import javafx.animation.Animation;\nimport javafx.animation.Interpolator;\nimport javafx.animation.KeyFrame;\nimport javafx.animation.KeyValue;\nimport javafx.animation.ParallelTransition;\nimport javafx.animation.PathTransition;\nimport javafx.animation.PathTransition.OrientationType;\nimport javafx.animation.Timeline;\nimport javafx.animation.Transition;\nimport javafx.application.Application;\nimport javafx.scene.Group;\nimport javafx.scene.PerspectiveCamera;\nimport javafx.scene.Scene;\nimport javafx.scene.effect.Bloom;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.input.ScrollEvent;\nimport javafx.scene.paint.Color;\nimport javafx.scene.paint.PhongMaterial;\nimport javafx.scene.shape.Box;\nimport javafx.scene.shape.Circle;\nimport javafx.scene.shape.Polygon;\nimport javafx.scene.shape.Shape;\nimport javafx.scene.shape.StrokeLineCap;\nimport javafx.scene.transform.Rotate;\nimport javafx.stage.Stage;\nimport javafx.util.Duration;\n\n/**\n * @see http://stackoverflow.com/a/37370840/230513\n */\npublic class Helix extends Application {\n\n    private static final double SIZE = 300;\n    private final Content content = Content.create(SIZE);\n\n    public void play() {\n        content.animation.play();\n    }\n\n    private static final class Content {\n\n        private static final Duration DURATION = Duration.seconds(4);\n        private static final Color COLOR = Color.AQUA;\n        private static final double WIDTH = 3;\n        private final Group group = new Group();\n        private final Rotate rx = new Rotate(0, Rotate.X_AXIS);\n        private final Rotate ry = new Rotate(0, Rotate.Y_AXIS);\n        private final Rotate rz = new Rotate(0, Rotate.Z_AXIS);\n        private final Box xAxis;\n        private final Box yAxis;\n        private final Box zAxis;\n        private final Shape circle;\n        private final Shape arrow;\n        private final Animation animation;\n\n        private static Content create(double size) {\n            Content c = new Content(size);\n            c.group.getChildren().addAll(c.arrow, c.circle,\n                c.xAxis, c.yAxis, c.zAxis);\n            c.group.getTransforms().addAll(c.rz, c.ry, c.rx);\n            c.group.setTranslateX(-size / 2);\n            c.group.setTranslateY(-size / 2);\n            c.group.setTranslateZ(size / 2);\n            c.rx.setAngle(35);\n            c.ry.setAngle(-45);\n            return c;\n        }\n\n        private Content(double size) {\n            xAxis = createBox(size, WIDTH, WIDTH);\n            yAxis = createBox(WIDTH, size, WIDTH);\n            zAxis = createBox(WIDTH, WIDTH, size);\n            circle = createCircle(size);\n            arrow = createShape();\n            animation = new ParallelTransition(\n                createTransition(circle, arrow),\n                createTimeline(size / 2));\n        }\n\n        private Circle createCircle(double size) {\n            Circle c = new Circle(size / 4);\n            c.setFill(Color.TRANSPARENT);\n            c.setStroke(COLOR);\n            return c;\n        }\n\n        private Box createBox(double w, double h, double d) {\n            Box b = new Box(w, h, d);\n            b.setMaterial(new PhongMaterial(COLOR));\n            return b;\n        }\n\n        private Shape createShape() {\n            Shape s = new Polygon(0, 0, -10, -10, 10, 0, -10, 10);\n            s.setStrokeWidth(WIDTH);\n            s.setStrokeLineCap(StrokeLineCap.ROUND);\n            s.setStroke(COLOR);\n            s.setEffect(new Bloom());\n            return s;\n        }\n\n        private Transition createTransition(Shape path, Shape node) {\n            PathTransition t = new PathTransition(DURATION, path, node);\n            t.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);\n            t.setCycleCount(Timeline.INDEFINITE);\n            t.setInterpolator(Interpolator.LINEAR);\n            return t;\n        }\n\n        private Timeline createTimeline(double size) {\n            Timeline t = new Timeline();\n            t.setCycleCount(Timeline.INDEFINITE);\n            t.setAutoReverse(true);\n            KeyValue keyX = new KeyValue(group.translateXProperty(), size);\n            KeyValue keyY = new KeyValue(group.translateYProperty(), size);\n            KeyValue keyZ = new KeyValue(group.translateZProperty(), -size);\n            KeyFrame keyFrame = new KeyFrame(DURATION.divide(2), keyX, keyY, keyZ);\n            t.getKeyFrames().add(keyFrame);\n            return t;\n        }\n    }\n\n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        primaryStage.setTitle("JavaFX 3D");\n        Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);\n        primaryStage.setScene(scene);\n        scene.setFill(Color.BLACK);\n        scene.setOnMouseMoved((final MouseEvent e) -> {\n            content.rx.setAngle(e.getSceneY() * 360 / scene.getHeight());\n            content.ry.setAngle(e.getSceneX() * 360 / scene.getWidth());\n        });\n        PerspectiveCamera camera = new PerspectiveCamera(true);\n        camera.setFarClip(SIZE * 6);\n        camera.setTranslateZ(-3 * SIZE);\n        scene.setCamera(camera);\n        scene.setOnScroll((final ScrollEvent e) -> {\n            camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());\n        });\n        primaryStage.show();\n        play();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在此相关示例中,黄色形状遵循Timeline由立方体正交轴旋转组成的动画,同时也遵循PathTransition沿着立方体面的边缘的动画。

\n\n

图像

\n\n
cube.setOnMouseMoved(new EventHandler<MouseEvent>() {\n    @Override\n    public void handle(final MouseEvent e) {\n        animation = new Timeline();\n        animation.getKeyFrames().addAll(\n            new KeyFrame(new Duration(2000),\n                new KeyValue(cube.rx.angleProperty(), e.getY()),\n                new KeyValue(cube.ry.angleProperty(), -e.getX()),\n                new KeyValue(cube.rz.angleProperty(), e.getY())\n            ));\n        animation.play();\n    }\n});\n\xe2\x80\xa6\npathBackFaceTransition = new PathTransition();\npathBackFaceTransition.setPath(rectangleBackFace);\n\xe2\x80\xa6\npathFrontFaceTransition = new PathTransition();\npathFrontFaceTransition.setPath(rectangleFrontFace);\n\xe2\x80\xa6\npublic void play() {\n    pathBackFaceTransition.play();\n    pathFrontFaceTransition.play();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,您可以使用针对缩放和平移属性的来模拟沿x、yz轴的运动。KeyValue再次参考动画基础知识,以下更改可TimelineEvents创建 3-D 形状来回移动的幻觉。

\n\n

图像

\n\n
//create a keyValue with factory: scaling the circle 2times\nKeyValue keyValueX = new KeyValue(stack.scaleXProperty(), 2);\nKeyValue keyValueY = new KeyValue(stack.scaleYProperty(), 2);\nKeyValue keyTransX = new KeyValue(stack.translateXProperty(), 100);\nKeyValue keyTransY = new KeyValue(stack.translateYProperty(), 100);\n\n//create a keyFrame, the keyValue is reached at time 2s\nDuration duration = Duration.millis(2000);\n//one can add a specific action when the keyframe is reached\nEventHandler<ActionEvent> onFinished = new EventHandler<ActionEvent>() {\n    @Override\n    public void handle(ActionEvent t) {\n        //reset counter\n        i = 0;\n    }\n};\nKeyFrame keyFrame = new KeyFrame(duration, onFinished,\n    keyValueX, keyValueY, keyTransX, keyTransY);\n
Run Code Online (Sandbox Code Playgroud)\n