パス上のノードの位置(期間)に応じて、PathTransition上のノードのサイズを変更したいと思います。アニメーションの例として、アニメーションのJavaFX2サークルパスを参照してください。
ノードのサイズは、パスの「中央」に到達するまで拡大し、その後縮小する必要があります。EventHandlerはないのに、アニメーションのonFinishedがあるのではないかと思います。
パス上のノードの位置(期間)に応じて、PathTransition上のノードのサイズを変更したいと思います。アニメーションの例として、アニメーションのJavaFX2サークルパスを参照してください。
ノードのサイズは、パスの「中央」に到達するまで拡大し、その後縮小する必要があります。EventHandlerはないのに、アニメーションのonFinishedがあるのではないかと思います。
使用する必要があるようです
public final ReadOnlyObjectProperty currentTimeProperty
PathTransitionクラスの。このプロパティの値の変更にリスナーを追加し、新しい値が表示されたら、double d = newDuration.toMillis()を呼び出して、パスのどの部分がなくなったかを判断し、アニメーションのフルタイムでdを分割します。
この回答の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);
}
}