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

时间:2013-01-06 13:43:12

标签: javafx-2

我想在PathTransition上更改Node的大小,从而减去Node在Path上的位置(持续时间)。 请参阅:JavaFX 2 circle path for animation作为动画示例。

Node的大小应该增长,直到达到Path的“中间”,然后缩小。我想知道没有EventHandler但是onFinished用于动画。

2 个答案:

答案 0 :(得分:1)

看起来应该使用

public final ReadOnlyObjectProperty currentTimeProperty

PathTransition类的

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

答案 1 :(得分: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定义一个自定义Interpolator,让您将比例映射到一个取决于当前路径位置或动画持续时间的值,但它并不是真正必要的,因为插值看起来不那么直观没有它给我好。

这是另一个带有自定义插补器的示例,它将根据路径沿路径的位置按比例缩放节点,使节点沿路径中途处于最大比例。它依赖于原始路径转换中的对称插值函数。样品尚未经过彻底测试,无法获得完美的正确性。除非绝对必要,否则我建议不使用自定义插补器的前一个示例。

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);
  }
}