使用StackPane对象的绝对坐标创建路径转换 [英] Create a path transition with absolute coordinates for a StackPane object
问题描述
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 PathElement
s 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对象的绝对坐标创建路径转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!