使用StackPane对象的绝对坐标创建路径转换 [英] Create a path transition with absolute coordinates for a StackPane object

查看:194
本文介绍了使用StackPane对象的绝对坐标创建路径转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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对象的绝对坐标创建路径转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆