1

我想在 PathTransition 上更改节点的大小,从而确定节点在路径上的位置(持续时间)。请参阅:动画的JavaFX 2 圆形路径作为动画的示例。

节点的大小应该增长到到达路径的“中间”,然后缩小。我想知道动画没有 ​​EventHandler 但 onFinished 。

4

2 回答 2

1

我在这个答案中更新了 Uluk 的示例,添加了一个 ParallelTransition,它在节点沿路径行进时对其进行缩放。对于路径的前半部分,节点的大小会增加。一旦节点位于路径的一半,它的大小就会缩小,直到它在圆的开始处达到它的原始大小。

动画在高速运行时确实有一种奇怪的前向运动模糊效果(你需要运行它才能看到这个),我无法真正解释。

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

public class ArcToScalingDemo extends Application {

  private PathTransition pathTransitionEllipse;
  private ParallelTransition scalingCirclePathTransition;

  private void init(Stage primaryStage) {
    Group root = new Group();
    primaryStage.setResizable(false);
    primaryStage.setScene(new Scene(root, 600, 460));

    // Ellipse path example
    Rectangle rect = new Rectangle(0, 0, 40, 40);
    rect.setArcHeight(10);
    rect.setArcWidth(10);
    rect.setFill(Color.ORANGE);
    root.getChildren().add(rect);

    Path path = createEllipsePath(200, 200, 50, 100, 45);
    root.getChildren().add(path);

    pathTransitionEllipse = PathTransitionBuilder.create()
        .duration(Duration.seconds(4))
        .path(path)
        .node(rect)
        .orientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT)
        .cycleCount(Timeline.INDEFINITE)
        .autoReverse(false)
        .build();

    // Circle path example
    Rectangle rect2 = new Rectangle(0, 0, 20, 20);
    rect2.setArcHeight(10);
    rect2.setArcWidth(10);
    rect2.setFill(Color.GREEN);
    root.getChildren().add(rect2);

    Path path2 = createEllipsePath(400, 200, 150, 150, 0);
    root.getChildren().add(path2);

    PathTransition pathTransitionCircle = PathTransitionBuilder.create()
        .duration(Duration.seconds(2))
        .path(path2)
        .node(rect2)
        .orientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT)
        .cycleCount(Timeline.INDEFINITE)
        .autoReverse(false)
        .build();

    ScaleTransition scaleTransition = ScaleTransitionBuilder.create()
        .duration(pathTransitionCircle.getDuration().divide(2))
        .fromX(1)
        .fromY(1)
        .toX(3)
        .toY(3)
        .cycleCount(Timeline.INDEFINITE)
        .autoReverse(true)
        .build();

    scalingCirclePathTransition = ParallelTransitionBuilder.create()
        .children(pathTransitionCircle, scaleTransition)
        .node(rect2)
        .build();
  }

  private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate) {
    ArcTo arcTo = new ArcTo();
    arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
    arcTo.setY(centerY - radiusY);
    arcTo.setSweepFlag(false);
    arcTo.setLargeArcFlag(true);
    arcTo.setRadiusX(radiusX);
    arcTo.setRadiusY(radiusY);
    arcTo.setXAxisRotation(rotate);

    Path path = PathBuilder.create()
        .elements(
            new MoveTo(centerX - radiusX, centerY - radiusY),
            arcTo,
            new ClosePath()) // close 1 px gap.
        .build();
    path.setStroke(Color.DODGERBLUE);
    path.getStrokeDashArray().setAll(5d, 5d);
    return path;
  }

  @Override
  public void start(Stage primaryStage) throws Exception {
    init(primaryStage);
    primaryStage.show();
    pathTransitionEllipse.play();
    scalingCirclePathTransition.play();
  }

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

节点在路径上的位置不一定与动画中经过的时间的相对持续时间相同,因为节点可能不会以恒定速度行进。此外,在此示例中,节点的相对比例可能与这些值中的任何一个都不完全匹配。您可以为 ScaleTransition 定义一个自定义插值器,它可以让您将比例映射到取决于当前路径位置或动画持续时间的值,但这并不是真正必要的,因为在我看来,插值看起来很好。

这是另一个带有自定义插值器的示例,它将根据节点沿路径的位置按比例缩放节点,从而使节点在路径的中途处于最大比例。它依赖于对称的原始路径过渡中的插值函数。样本未经过彻底测试以确保完全正确。除非绝对必要,否则我会推荐前面的示例,而不需要自定义插值器。

public class ArcToInterpolation Demo extends Application {

  class HalfInterpolator extends Interpolator {
    final Interpolator source;

    HalfInterpolator(Interpolator source) {
      this.source = source;
    }

    @Override protected double curve(double t) {
      return t <= 0.5
          ? source.interpolate(0.0, 1.0, t) * 2
          : source.interpolate(0.0, 1.0, 1 - t) * 2;
    }
  }

  private ParallelTransition scalingCirclePathTransition;

  private void init(Stage primaryStage) {
    Group root = new Group();
    primaryStage.setResizable(false);
    primaryStage.setScene(new Scene(root, 600, 460));

    // Circle path example
    Rectangle rect2 = new Rectangle(0, 0, 20, 20);
    rect2.setArcHeight(10);
    rect2.setArcWidth(10);
    rect2.setFill(Color.GREEN);
    root.getChildren().add(rect2);

    Path path2 = createEllipsePath(400, 200, 150, 150, 0);
    root.getChildren().add(path2);

    PathTransition pathTransitionCircle = PathTransitionBuilder.create()
        .duration(Duration.seconds(10))
        .interpolator(Interpolator.EASE_BOTH)
        .path(path2)
        .node(rect2)
        .orientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT)
        .cycleCount(Timeline.INDEFINITE)
        .autoReverse(false)
        .build();

    ScaleTransition scaleTransition = ScaleTransitionBuilder.create()
        .duration(pathTransitionCircle.getDuration())
        .interpolator(new HalfInterpolator(pathTransitionCircle.getInterpolator()))
        .fromX(1)
        .fromY(1)
        .toX(4)
        .toY(4)
        .cycleCount(Timeline.INDEFINITE)
        .autoReverse(false)
        .build();

    scalingCirclePathTransition = ParallelTransitionBuilder.create()
        .children(pathTransitionCircle, scaleTransition)
        .node(rect2)
        .build();
  }

  private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate) {
    ... as in previous sample
  }

  @Override
  public void start(Stage primaryStage) throws Exception {
    init(primaryStage);
    primaryStage.show();
    scalingCirclePathTransition.play();
  }

  public static void main(String[] args) {
    launch(args);
  }
}
于 2013-01-06T21:53:52.980 回答
1

看起来你应该使用

public final ReadOnlyObjectProperty currentTimeProperty

PathTransition 类的。您可以在此属性的值更改时添加侦听器,当出现新值时,调用 double d = newDuration.toMillis(),并确定路径的哪一部分消失了,将 d 划分为动画的全部时间。

于 2013-01-06T14:10:53.160 回答