OrangeBlock 是一个带有文本的橙色块。它实现为 StackPane ,其中包含矩形顶部的文本。 (这种方法在 StackPane的文档中进行了演示。)
OrangeBlock is an orange block with text inside. It is implemented as a StackPane that contains text on top of a rectangle. (This approach is demonstrated in the documentation for StackPane.)
我在坐标(100,80)处放置了 OrangeBlock 试图让它平稳地移动到一些目标坐标。不幸的是,我在路上遇到了一个令人讨厌的问题:
I've placed an OrangeBlock at coordinates (100, 80) and am now trying to make it travel smoothly to some target coordinates. Unfortunately I get a nasty bump in my path:
由于某种原因, PathElement 中的坐标是相对于橙色块解释的。
For some reason the coordinates in the PathElements are interpreted relative to the orange block.
这是为什么?如何让我的 OrangeBlock 沿着绝对坐标的路径行进?下面的最小工作示例。
Why is this? And how can I make my OrangeBlock travel along a path with absolute coordinates? Minimal working example below.
import javafx.animation.PathTransition; import javafx.application.Application; import javafx.scene.*; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.*; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.util.Duration; public class PathTransitionExample extends Application { @Override public void start(Stage primaryStage) throws Exception { Group root = new Group(); OrangeBlock block = new OrangeBlock(60, 40); block.relocate(100, 80); root.getChildren().add(block); PathTransition transition = newPathTransitionTo(block, 460, 320); primaryStage.setScene(new Scene(root, 600, 400)); primaryStage.show(); transition.play(); } private static PathTransition newPathTransitionTo(OrangeBlock block, double toX, double toY) { double fromX = block.getLayoutX(); double fromY = block.getLayoutY(); Path path = new Path(); path.getElements().add(new MoveTo(fromX, fromY)); path.getElements().add(new LineTo(toX, toY)); PathTransition transition = new PathTransition(); transition.setPath(path); transition.setNode(block); transition.setDelay(Duration.seconds(1)); transition.setDuration(Duration.seconds(2)); return transition; } public static void main(String[] args) { launch(args); } private static class OrangeBlock extends StackPane { public OrangeBlock(int width, int height) { Rectangle rectangle = new Rectangle(width, height, Color.ORANGE); Text text = new Text("Block"); getChildren().addAll(rectangle, text); } } }推荐答案
我出于好奇而调试了JavaFX代码。似乎你运气不好找到合适的解决方案。以下是发生的情况:
I debugged the JavaFX code out of curiosity. Seems like you are out of luck with a proper solution. Here's what happens:
PathTransition代码有一个方法interpolate(double frac),其中包括:
The PathTransition code has a method interpolate(double frac) which includes:
cachedNode.setTranslateX(x - cachedNode.impl_getPivotX()); cachedNode.setTranslateY(y - cachedNode.impl_getPivotY());impl_getPivotX()和impl_getPivotY()方法包含:
The impl_getPivotX() and impl_getPivotY() methods contain this:
public final double impl_getPivotX() { final Bounds bounds = getLayoutBounds(); return bounds.getMinX() + bounds.getWidth()/2; } public final double impl_getPivotY() { final Bounds bounds = getLayoutBounds(); return bounds.getMinY() + bounds.getHeight()/2; }因此PathTransition始终使用节点的中心进行计算。换句话说,这适用于e。 G。一个Circle节点,但没有e。 G。一个Rectangle节点。此外,您需要layoutBounds,因此必须在边界可用后创建PathTransition。
So the PathTransition always uses the center of your node for the calculation. In other words this works with e. g. a Circle node, but not with e. g. a Rectangle node. Moreover you need the layoutBounds, so the PathTransition must be created after the bounds were made available.
您可以在PathTransition代码中看到计算都是相对的并且已经涉及布局位置。所以在你的行中你必须考虑这个。
You can see in the PathTransition code that the calculations are all relative and already involve the layout position. So in your lineTo you have to consider this.
值得注意的是LineTo类有一个方法setAbsolut(boolean)。但是它并没有解决你的问题。
Worth noting is that the LineTo class has a method setAbsolut(boolean). However it doesn't solve your problem.
所以我的问题解决方案是
So my solution to your problem would be
- 在主要阶段可见后创建PathTransition
- 修改moveTo和lineTo参数
这对我有用(我添加了一个Rectangle形状来直观地识别正确的边界):
This works for me (I added a Rectangle shape to identify the proper bounds visually):
public class PathTransitionExampleWorking2 extends Application { @Override public void start(Stage primaryStage) throws Exception { Group root = new Group(); Rectangle rect = new Rectangle( 100, 80, 460-100+60, 320-80+40); root.getChildren().add(rect); OrangeBlock block = new OrangeBlock(60, 40); block.relocate( 100, 80); root.getChildren().add(block); primaryStage.setScene(new Scene(root, 600, 400)); primaryStage.show(); // layout bounds are used in path transition => PathTransition creation must happen when they are available PathTransition transition = newPathTransitionTo(block, 460, 320); transition.play(); } private static PathTransition newPathTransitionTo(OrangeBlock block, double toX, double toY) { double fromX = block.getLayoutBounds().getWidth() / 2; double fromY = block.getLayoutBounds().getHeight() / 2; toX -= block.getLayoutX() - block.getLayoutBounds().getWidth() / 2; toY -= block.getLayoutY() - block.getLayoutBounds().getHeight() / 2; Path path = new Path(); path.getElements().add(new MoveTo(fromX, fromY)); path.getElements().add(new LineTo(toX, toY)); PathTransition transition = new PathTransition(); transition.setPath(path); transition.setNode(block); transition.setDelay(Duration.seconds(1)); transition.setDuration(Duration.seconds(2)); return transition; } public static void main(String[] args) { launch(args); } private static class OrangeBlock extends StackPane { public OrangeBlock(int width, int height) { Rectangle rectangle = new Rectangle(width, height, Color.ORANGE); Text text = new Text("Block"); getChildren().addAll(rectangle, text); } } }编辑:另一个解决方案是使用它而不是MoveTo和LineTo:
edit: another solution would be to use this instead of MoveTo and LineTo:
public static class MoveToAbs extends MoveTo { public MoveToAbs( Node node) { super( node.getLayoutBounds().getWidth() / 2, node.getLayoutBounds().getHeight() / 2); } } public static class LineToAbs extends LineTo { public LineToAbs( Node node, double x, double y) { super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2); } }注意:你仍然需要创建创建primaryStage后的PathTransition。
Note: You still have to create the PathTransition after the primaryStage was created.
编辑:这是块移动到鼠标点击位置的另一个例子:
edit: here's another example with the block moving to the position of the mouse-click:
public class PathTransitionExample extends Application { @Override public void start(Stage primaryStage) throws Exception { Group root = new Group(); OrangeBlock block = new OrangeBlock(60, 40); block.relocate(100, 80); root.getChildren().add(block); Label label = new Label( "Click on scene to set destination"); label.relocate(0, 0); root.getChildren().add(label); Scene scene = new Scene(root, 600, 400); scene.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>() { PathTransition transition; { transition = new PathTransition(); transition.setNode(block); transition.setDuration(Duration.seconds(2)); } @Override public void handle(Event event) { transition.stop(); setPositionFixed(block.getLayoutX() + block.getTranslateX(), block.getLayoutY() + block.getTranslateY()); double x = ((MouseEvent) event).getX(); double y = ((MouseEvent) event).getY(); Path path = new Path(); path.getElements().add(new MoveToAbs( block)); path.getElements().add(new LineToAbs( block, x, y)); transition.setPath(path); transition.play(); } private void setPositionFixed( double x, double y) { block.relocate(x, y); block.setTranslateX(0); block.setTranslateY(0); } }); primaryStage.setScene( scene); primaryStage.show(); PathTransition transition = newPathTransitionTo(block, 460, 320); transition.play(); } private static PathTransition newPathTransitionTo(OrangeBlock block, double toX, double toY) { Path path = new Path(); path.getElements().add(new MoveToAbs( block)); path.getElements().add(new LineToAbs( block, toX, toY)); PathTransition transition = new PathTransition(); transition.setPath(path); transition.setNode(block); transition.setDelay(Duration.seconds(1)); transition.setDuration(Duration.seconds(2)); return transition; } public static void main(String[] args) { launch(args); } private static class OrangeBlock extends StackPane { public OrangeBlock(int width, int height) { Rectangle rectangle = new Rectangle(width, height, Color.ORANGE); Text text = new Text("Block"); getChildren().addAll(rectangle, text); } } public static class MoveToAbs extends MoveTo { public MoveToAbs( Node node) { super( node.getLayoutBounds().getWidth() / 2, node.getLayoutBounds().getHeight() / 2); } } public static class LineToAbs extends LineTo { public LineToAbs( Node node, double x, double y) { super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2); } } }更多推荐
使用StackPane对象的绝对坐标创建路径转换
发布评论