0

I was creating animated buttons and there is one serious problem - event OnMouseExited works incorrectly.

When I hover mouse on button not very fast - everything works ok. But when I hover very fast - the event OnMouseExited don't fire. You can see this strange behavior on the video below. https://youtu.be/Uz0vgIK21RU

Here is my code:

public class MenuButton extends JFXButton {
 private final int ICON_SIZE_MAX = 50;
 private final int ICON_SIZE_MIN = 40;
 private final int BUTTON_SIZE = 130;
 private final int BUTTON_PANE_WIDTH = 114;
 private final int BUTTON_PANE_HEIGHT = 122;
 JFXButton btn = new JFXButton();
 Pane p = new Pane();
 GlyphIcon icon = null;
 Label text = new Label();
 public MenuButton(String name, FontAwesomeIcon fa_icon, EmojiOne eo_icon, String color, String rippler, VENTO.GUI gui) {
 btn.setPrefWidth(BUTTON_SIZE);
 btn.setPrefHeight(BUTTON_SIZE);
 btn.setStyle("-fx-background-color: " + color + ";");
 btn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
 btn.setRipplerFill(Paint.valueOf(rippler));
 btn.setCursor(Cursor.HAND);
 p.setPrefWidth(BUTTON_PANE_WIDTH);
 p.setPrefHeight(BUTTON_PANE_HEIGHT);
 if (fa_icon != null) {
 icon = new FontAwesomeIconView(fa_icon);
 } else if (eo_icon != null) {
 icon = new EmojiOneView(eo_icon);
 }
 if (icon != null) {
 icon.setGlyphSize(ICON_SIZE_MAX);
 icon.setFill(Paint.valueOf("#ffffff"));
 icon.setLayoutX(p.getPrefWidth() / 2 - icon.getGlyphSize().intValue() / 2);
 icon.setLayoutY(p.getPrefHeight() / 2 - icon.getGlyphSize().intValue() / 2 + 43);
 }
 text.setText(name);
 text.setFont(Font.font("System", FontWeight.BOLD, 18));
 text.setTextFill(Paint.valueOf("#ffffff"));
 text.setPrefWidth(p.getPrefWidth());
 text.setLayoutX(0);
 text.setLayoutY(75);
 text.setAlignment(Pos.CENTER);
 text.setOpacity(0);
 p.getChildren().setAll(icon, text);
 btn.setGraphic(p);
 GlyphIcon finalIcon = icon;
 btn.setOnMouseEntered(e -> {
 if (finalIcon.getGlyphSize().intValue() != ICON_SIZE_MAX) {
 return;
 }
 showMenuButton();
 });
 btn.setOnMouseExited(e -> {
 if (finalIcon.getGlyphSize().intValue() != ICON_SIZE_MIN) {
 return;
 }
 hideMenuButton();
 });
 btn.setOnAction(e -> {
 //TODO
 });
 }
 private void showMenuButton() {
 Animation animation1 = new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(icon.glyphSizeProperty(), ICON_SIZE_MIN)));
 animation1.play();
 Animation animation2 = new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(icon.layoutXProperty(), p.getPrefWidth() / 2 - ICON_SIZE_MIN / 2)));
 animation2.play();
 Animation animation3 = new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(icon.layoutYProperty(), p.getPrefHeight() / 2 - ICON_SIZE_MIN / 2 + 43 - 20)));
 animation3.play();
 Animation animation4 = new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(text.opacityProperty(), 1.0)));
 animation4.setDelay(Duration.millis(50));
 animation4.play();
 }
 private void hideMenuButton() {
 Animation animation4 = new Timeline(new KeyFrame(Duration.millis(50), new KeyValue(text.opacityProperty(), 0.0)));
 animation4.play();
 Animation animation3 = new Timeline(new KeyFrame(Duration.millis(50), new KeyValue(icon.layoutYProperty(), p.getPrefHeight() / 2 - ICON_SIZE_MAX / 2 + 43)));
 animation3.setDelay(Duration.millis(25));
 animation3.play();
 Animation animation2 = new Timeline(new KeyFrame(Duration.millis(50), new KeyValue(icon.layoutXProperty(), p.getPrefWidth() / 2 - ICON_SIZE_MAX / 2)));
 animation2.setDelay(Duration.millis(25));
 animation2.play();
 Animation animation1 = new Timeline(new KeyFrame(Duration.millis(50), new KeyValue(icon.glyphSizeProperty(), ICON_SIZE_MAX)));
 animation1.setDelay(Duration.millis(25));
 animation1.play();
 }
}

And I don't know what to do... Hope, somebody could help me. Thank's in advance.

asked Aug 18, 2018 at 16:02
3
  • 1
    I think its because the of animation which ends after the mouse has left the tile (and therefore onFinnished is called after that). What halpens if you remove the anmiation/set its duration to 0? Commented Aug 18, 2018 at 16:48
  • I set duration to 0 of animation onMouseExit and it don't work. I need to return text and picture on tile that was before hovering on the tile. I also did this and event onMouseExit also don't work if move your mouse very fast. private void hideMenuButton() { text.setOpacity(0); icon.setLayoutY(p.getPrefHeight() / 2 - ICON_SIZE_MAX / 2 + 43); icon.setLayoutX(p.getPrefWidth() / 2 - ICON_SIZE_MAX / 2); icon.setGlyphSize(ICON_SIZE_MAX); } Commented Aug 18, 2018 at 17:27
  • You already found a hacky way to solve your problem. But I want to point out that the visual problems is caused by the animation in the onMouseEntered event, not the onMouseExited. Although reëntering the tile quickly will cause simular problems, only the other way around when not solved properly Commented Aug 18, 2018 at 18:03

2 Answers 2

1

I think its because your animations will mess up when the mouse leaves before the animation is finnished. A way to solve this is to store the animation locally and modify it accordingly. e.g.

protected Animation animation;
protected void initAnimation()
{
 this.animation = new ParallelTransition(
 new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(icon.glyphSizeProperty(), ICON_SIZE_MIN))),
 new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(icon.layoutXProperty(), p.getPrefWidth() / 2 - ICON_SIZE_MIN / 2))),
 new Timeline(new KeyFrame(Duration.millis(100), new KeyValue(icon.layoutYProperty(), p.getPrefHeight() / 2 - ICON_SIZE_MIN / 2 + 43 - 20))),
 new Timeline(new KeyFrame(Duration.millis(50)), new KeyFrame(Duration.millis(100), new KeyValue(text.opacityProperty(), 1.0))));
}
private void showMenuButtons()
{
 // set animation to go foreward
 this.animation.setRate(1.0);
 // play from start when not running
 if(this.animation.getStatus() != Animation.Status.RUNNING)
 this.animation.playFromStart();
}
private void hideMenuButtons()
{
 // set animation to go backwards
 this.animation.setRate(-1.0);
 // play from end when not running
 if(this.animation.getStatus() != Animation.Status.RUNNING)
 animation.playFrom(animation.getCycleDuration());
}
answered Aug 18, 2018 at 17:31
Sign up to request clarification or add additional context in comments.

Comments

0

I have found the solution.

  1. I have made global variables of Animation.
  2. I changed my method "hideMenuButton()": I check if animation running - I stop it and then run animation of hiding button.

    if (animation4.getStatus() == Animation.Status.RUNNING) { animation4.stop(); }

Thank you everybody for helping me :)

answered Aug 18, 2018 at 17:44

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.