JavaFX 2动画:缩放节点取决于PathTransition持续时间



我想根据节点在路径上的位置(持续时间)改变PathTransition上节点的大小。参见:JavaFX 2圆路径动画作为动画的例子。

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

我在这个回答中更新了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);
  }
}

看起来你应该使用

public final ReadOnlyObjectProperty currentTimeProperty

PathTransition类的

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

最新更新