有没有办法在多个节点上检测同一事件?

Cry*_*nda 1 java javafx fxml

我正在研究一种元素周期表,有一个事件最终,当您将鼠标悬停时,会放大一点所述元素并显示更多信息,但事实是它并没有真正完成这项工作很好。可以说是跟踪,因为如果鼠标移动得有点快,或者很快离开位于边缘的面板,它就不会检测到鼠标的退出。这是鼠标进入或退出时应执行的动画代码:

public void onScale(int i){
    double currentScaleX = Panes[i].getScaleX();
    double currentScaleY = Panes[i].getScaleY();//stores the current scale
    
    if (currentScaleX == 1.0 && currentScaleY == 1.0) {//verify that said scale is the original (equal to 1)
      

        TranslateTransition translate = new TranslateTransition(Duration.millis(50), Symbol[i]);
        translate.setToY(-4);
        translate.playFromStart();//move the symbol up a little

        TranslateTransition tratn = new TranslateTransition(Duration.millis(50), AtomicNumber[i]);
        tratn.setToY(-2);
        tratn.playFromStart();

        TranslateTransition tratm = new TranslateTransition(Duration.millis(50), AtomicMass[i]);
        tratm.setToY(-2);
        tratm.playFromStart();

        TranslateTransition trnm = new TranslateTransition(Duration.millis(50), Name[i]);
        trnm.setToY(3);
        trnm.playFromStart();
        
        AtomicMass[i].setVisible(true);
        OxidationNumber[i].setVisible(true);//makes the atomic mass and oxidation number visible
        
        ScaleTransition scale = new ScaleTransition(Duration.millis(100), Panes[i]);
        scale.setToX(1.5);
        scale.setToY(1.5);
        scale.playFromStart();
        //The panel is brought to the foreground, (note that this will not affect its position in the childlist unlike the .tofront method)
        Panes[i].setViewOrder(-1.0);
        Panes[i].getParent().setViewOrder(-1.0);
        
        
        
    }
}

public void offScale(int i){
    double currentScaleX = Panes[i].getScaleX();
    double currentScaleY = Panes[i].getScaleY();
    
    if (currentScaleX > 1.0 && currentScaleY > 1.0) {
        
        TranslateTransition translate = new TranslateTransition(Duration.millis(50), Symbol[i]);
        translate.setToY(0);
        translate.playFromStart();

        TranslateTransition tratn = new TranslateTransition(Duration.millis(50), AtomicNumber[i]);
        tratn.setToY(0);
        tratn.playFromStart();

        TranslateTransition tratm = new TranslateTransition(Duration.millis(50), AtomicMass[i]);
        tratm.setToY(0);
        tratm.playFromStart();
        
        TranslateTransition trnm = new TranslateTransition(Duration.millis(50), Name[i]);
        trnm.setToY(0);
        trnm.playFromStart();

        AtomicMass[i].setVisible(false);
        OxidationNumber[i].setVisible(false);
        
        ScaleTransition scale = new ScaleTransition(Duration.millis(100), Panes[i]);
        scale.setToX(1.0);
        scale.setToY(1.0);
        scale.playFromStart();
        Panes[i].setViewOrder(0.0);
        Panes[i].getParent().setViewOrder(0.0);
    }
    
}
Run Code Online (Sandbox Code Playgroud)

那么,您有什么建议吗?我正在使用窗格向量。PS:我现在用来完成类似工作的方法是创建另一个方法,稍后在初始化整个表的另一个方法中调用该方法。调用动画的方法:

    @FXML
private void MouseEntered(MouseEvent event) {
    Pane pane = (Pane) event.getSource();//creates a local variable based on the pane
    onScale(Integer.parseInt(pane.getId()));
    for(int i=1; i<=118; i++){
        offScale(i);
    }
}

@FXML
private void MouseExited(MouseEvent event) {
    Pane pane = (Pane) event.getSource();
    offScale(Integer.parseInt(pane.getId()));
    
}
Run Code Online (Sandbox Code Playgroud)

在初始化方法的一部分中有一个循环,它分配如下事件:

    Panes[i].setOnMouseEntered(this::MouseEntered);
    Panes[i].setOnMouseExited(this::MouseExited);
Run Code Online (Sandbox Code Playgroud)

Sla*_*law 7

无论移动鼠标的速度如何或鼠标是否离开整个窗口,都应始终传递MOUSE_ENTERED事件。您没有提供一个最小的、完整的示例,所以我不能绝对确定问题是什么。但是,我认为问题在于您正在为每个“方向”创建两个单独的动画。这意味着“按比例”动画可以与“非比例”动画并行运行,从而导致结果不一致。同样奇怪的是,您手动注册了一个用 注释的事件处理程序方法,但这是否会导致问题取决于您未显示的其余代码。MOUSE_EXITED@FXML

您应该使用单个动画并操纵其rate属性。这不仅可以防止两个动画发生冲突,而且可以让动画在播放过程中轻松切换方向,而不会影响动画的速度。ParallelTransition如果您希望多个动画并行运行,我还建议您使用 a ,而不是单独播放每个动画。


例子

这是一个演示上面讨论的建议的示例。最后有一个 GIF 显示了代码的输出。

动画是在类CellAnimation中嵌套的类中实现的Cell。您会注意到有关“单元格”的逻辑viewOrder比您当前的代码稍微复杂一些。本质上,逻辑是:

  • 上升单元格:视图顺序从0-1立即,然后 到-2动画的中间点。

  • 降低单元格:视图顺序从-2-1位于动画的中间点,然后转到0动画的结尾处。请注意,如果单元格未升起至少一半,则查看顺序将从 开始-1

这种逻辑给出了上升单元和下降单元在中间标记处相互“穿过”的错觉。这也意味着动画单元始终呈现在所有非动画单元上方。换句话说,这一切都让动画看起来更加自然。

主要.java:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class Main extends Application {

    private static final int ROWS = 5;
    private static final int COLUMNS = 5;

    @Override
    public void start(Stage primaryStage) throws Exception {
        var root = new GridPane();
        root.setPadding(new Insets(30));
        root.setHgap(5);
        root.setVgap(5);
        root.setAlignment(Pos.CENTER);

        for (int row = 0; row < ROWS; row++) {
            for (int col = 0; col < COLUMNS; col++) {
                var cell = new Cell(row * COLUMNS + col);
                root.add(cell.getNode(), col, row);
            }
        }

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}
Run Code Online (Sandbox Code Playgroud)

细胞.java:

import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.ScaleTransition;
import javafx.animation.TranslateTransition;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

public class Cell {

    private final StackPane container;
    private final Label indexLabel;
    private final Label moreInfoLabel;

    public Cell(int index) {
        var square = new Rectangle(75, 75, Color.WHITE);
        square.setStroke(Color.BLACK);
        square.setStrokeWidth(1);

        indexLabel = new Label(Integer.toString(index));

        moreInfoLabel = new Label("More info!");
        moreInfoLabel.setOpacity(0);

        container = new StackPane(square, indexLabel, moreInfoLabel);

        // must be instantiated after creating nodes
        var animation = new CellAnimation();
        container.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> {
            e.consume();
            animation.playForward();
        });
        container.addEventHandler(MouseEvent.MOUSE_EXITED, e -> {
            e.consume();
            animation.playBackward();
        });
    }

    public Node getNode() {
        return container;
    }

    private class CellAnimation {

        private static final Duration DURATION = Duration.millis(250);

        private final ParallelTransition transition;

        private boolean rising;
        private boolean changeViewOrder;

        CellAnimation() {
            var toCorner = createToCornerAnimation();
            var fadeInOut = createFadeInAndOutAnimation();
            var scale = createScaleAnimation();
            transition = new ParallelTransition(toCorner, fadeInOut, scale);

            transition.currentTimeProperty().addListener(obs -> {
                if (changeViewOrder) {
                    var currentTime = transition.getCurrentTime();
                    var halfwayTime = transition.getCycleDuration().divide(2);
                    int compare = currentTime.compareTo(halfwayTime);
                    if (rising && compare >= 0) {
                        changeViewOrder = false;
                        container.setViewOrder(-2);
                    } else if (!rising && compare <= 0) {
                        changeViewOrder = false;
                        container.setViewOrder(-1);
                    }
                }
            });

            transition.statusProperty().addListener((obs, oldVal, newVal) -> {
                if (newVal == Animation.Status.RUNNING && rising) {
                    container.setViewOrder(-1);
                } else if (newVal == Animation.Status.STOPPED && !rising) {
                    container.setViewOrder(0);
                }
            });
        }

        void playForward() {
            rising = true;
            changeViewOrder = true;

            transition.setRate(1);
            transition.play();
        }

        void playBackward() {
            rising = false;
            changeViewOrder = true;

            transition.setRate(-1);
            transition.play();
        }

        private Animation createToCornerAnimation() {
            var toCorner = new TranslateTransition(DURATION, indexLabel);
            toCorner.setFromX(0);
            toCorner.setFromY(0);
            toCorner.toXProperty().bind(indexLabel.layoutXProperty().negate().add(5));
            toCorner.toYProperty().bind(indexLabel.layoutYProperty().negate().add(5));
            return toCorner;
        }

        private Animation createFadeInAndOutAnimation() {
            var fadeInOut = new FadeTransition(DURATION, moreInfoLabel);
            fadeInOut.setFromValue(0);
            fadeInOut.setToValue(1);
            return fadeInOut;
        }

        private Animation createScaleAnimation() {
            var scale = new ScaleTransition(DURATION, container);
            scale.setFromX(1.0);
            scale.setFromY(1.0);
            scale.setToX(1.5);
            scale.setToY(1.5);
            return scale;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

GIF 演示示例

  • 如果您在播放动画时(即发生 MouseEnter 时)设置[视图顺序](https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Node.html#viewOrderProperty),这可能会更平滑一些。正确实现后,它应该允许动画节点不会与网格右侧或底部的相邻节点重叠。 (2认同)