2015-09-20 1 views
1

Я не понимаю, почему это происходит. В настоящее время я пытаюсь перетащить узел (Canvas), щелкнув его правой кнопкой мыши, нажав правой кнопкой мыши и перемещая мышь. Сначала он плавный, но затем он начинает получать этот странный джиттер. Это происходит только тогда, когда я удерживаю кнопку мыши. Если вы отпустите его, тогда он вернется к норме (но может очень быстро получить дрожь снова).Точность определения местоположения JavaFX MouseEvent ухудшается с течением времени, приводит к дрожанию узла.

С некоторой отладкой кажется, что чем больше вы его перемещаете, тем больше вы получаете эту «неточность» между каждым событием перетаскивания, когда положение мыши больше не синхронизируется. Конечным результатом является то, что он переворачивает flops взад и вперед, что выглядит очень плохо. используется

Код:

Main.java

import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.stage.Stage; 
import javafx.scene.Scene; 
import javafx.scene.layout.Pane; 

public class Main extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     try { 
      FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("CanvasPane.fxml")); 
      Pane root = fxmlLoader.load(); 
      Scene scene = new Scene(root, 512, 512); 
      primaryStage.setScene(scene); 
      primaryStage.show(); 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Controller.java

import javafx.fxml.FXML; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.canvas.GraphicsContext; 
import javafx.scene.input.MouseButton; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.StrokeLineCap; 
import javafx.scene.shape.StrokeLineJoin; 

public class Controller { 

    @FXML 
    private Pane rootPane; 

    @FXML 
    private Canvas canvas; 

    private double mouseX; 

    private double mouseY; 

    @FXML 
    private void initialize() { 
     GraphicsContext gc = canvas.getGraphicsContext2D(); 

     // Set the writing pen. 
     gc.setLineCap(StrokeLineCap.ROUND); 
     gc.setLineJoin(StrokeLineJoin.ROUND); 
     gc.setLineWidth(1); 
     gc.setStroke(Color.BLACK); 

     // Set the background to be transparent. 
     gc.setFill(Color.BLUE); 
     gc.fillRect(0, 0, 256, 256); 

     // Handle moving the canvas. 
     canvas.setOnMousePressed(e -> { 
      if (e.getButton() == MouseButton.PRIMARY) { 
       gc.moveTo(e.getX(), e.getY()); 
      } else if (e.getButton() == MouseButton.SECONDARY) { 
       mouseX = e.getX(); 
       mouseY = e.getY(); 
      } 
     }); 

     canvas.setOnMouseDragged(e -> { 
      if (e.getButton() == MouseButton.PRIMARY) { 
       gc.lineTo(e.getX(), e.getY()); 
       gc.stroke(); 
      } else if (e.getButton() == MouseButton.SECONDARY) { 
       double newMouseX = e.getX(); 
       double newMouseY = e.getY(); 
       double deltaX = newMouseX - mouseX; 
       double deltaY = newMouseY - mouseY; 
       canvas.setLayoutX(canvas.getLayoutX() + deltaX); 
       canvas.setLayoutY(canvas.getLayoutY() + deltaY); 
       mouseX = newMouseX; 
       mouseY = newMouseY; 
       // Debug: Why is this happening? 
       if (Math.abs(deltaX) > 4 || Math.abs(deltaY) > 4) 
        System.out.println(deltaX + " " + deltaY); 
      } 
     }); 
    } 
} 

CanvasPane.fxml

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.canvas.*?> 
<?import java.lang.*?> 
<?import javafx.scene.layout.*?> 
<?import javafx.scene.layout.Pane?> 

<Pane fx:id="rootPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="0.0" minWidth="0.0" prefHeight="512.0" prefWidth="512.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="your-controller-here"> 
    <children> 
     <Canvas fx:id="canvas" height="256.0" layoutX="122.0" layoutY="107.0" width="256.0" /> 
    </children> 
</Pane> 

ИНСТРУКЦИЯ ПО ИСПОЛЬЗОВАНИЮ

1) Запустить приложение

2) Щелкните правой кнопкой мыши на холсте и переместить его очень немного (например, только один пиксел в секунду)

3) Переместить это немного быстрее, поэтому отладочные отпечатки

4) Когда вы возвращаетесь к медленному движению, по какой-то причине вы увидите, как он прыгает взад-вперед (как дельта-движение между старым и новым положением больше 5 пикселей), хотя вы только переместили его на один пиксель.

Затем он пытается исправить себя в следующий раз, когда вы двигаетесь, что придает ему уродливый нервный взгляд.


Причина я смущен, потому что бывают случаи, когда он прекрасно работает, а затем точность только падает, как вы продолжайте перетаскивать.

Я сделал что-то неправильно, или это потенциальная ошибка?

ответ

2

Координаты, которые вы измеряете MouseEvent.getX() и MouseEvent.getY(), относятся к узлу, на котором произошло событие: т. Е. Относительно вашего Canvas. Поскольку вы перемещаете холст, старые координаты теперь неверны (поскольку они относились к старой позиции, а не к новой). Следовательно, ваши значения для deltaX и deltaY неверны.

Чтобы исправить это, просто измерьте относительно того, что является «фиксированным», например. Scene. Вы можете сделать это, используя MouseEvent.getSceneX() и MouseEvent.getSceneY().

import javafx.fxml.FXML; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.canvas.GraphicsContext; 
import javafx.scene.input.MouseButton; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.StrokeLineCap; 
import javafx.scene.shape.StrokeLineJoin; 

public class CanvasDragController { 

    @FXML 
    private Pane rootPane; 

    @FXML 
    private Canvas canvas; 

    private double mouseX; 

    private double mouseY; 

    @FXML 
    private void initialize() { 
     GraphicsContext gc = canvas.getGraphicsContext2D(); 

     // Set the writing pen. 
     gc.setLineCap(StrokeLineCap.ROUND); 
     gc.setLineJoin(StrokeLineJoin.ROUND); 
     gc.setLineWidth(1); 
     gc.setStroke(Color.BLACK); 

     // Set the background to be transparent. 
     gc.setFill(Color.BLUE); 
     gc.fillRect(0, 0, 256, 256); 

     // Handle moving the canvas. 
     canvas.setOnMousePressed(e -> { 
      if (e.getButton() == MouseButton.PRIMARY) { 
       gc.moveTo(e.getX(), e.getY()); 
      } else if (e.getButton() == MouseButton.SECONDARY) { 
       mouseX = e.getSceneX(); 
       mouseY = e.getSceneY(); 
      } 
     }); 

     canvas.setOnMouseDragged(e -> { 
      if (e.getButton() == MouseButton.PRIMARY) { 
       gc.lineTo(e.getX(), e.getY()); 
       gc.stroke(); 
      } else if (e.getButton() == MouseButton.SECONDARY) { 
       double newMouseX = e.getSceneX(); 
       double newMouseY = e.getSceneY(); 
       double deltaX = newMouseX - mouseX; 
       double deltaY = newMouseY - mouseY; 
       canvas.setLayoutX(canvas.getLayoutX() + deltaX); 
       canvas.setLayoutY(canvas.getLayoutY() + deltaY); 
       mouseX = newMouseX; 
       mouseY = newMouseY; 
       // Why is this happening? 
       if (Math.abs(deltaX) > 4 || Math.abs(deltaY) > 4) 
        System.out.println(deltaX + " " + deltaY); 
      } 
     }); 
    } 
} 

Альтернативный подход (который не является столь же интуитивно, для меня по крайней мере), чтобы использовать MouseEvent.getX() и MouseEvent.getY(), но не обновлять «последние координаты» мыши. Способ думать об этом заключается в том, что, когда узел перемещается, вы хотите, чтобы он не двигался по отношению к мыши - другими словами, вы хотите, чтобы узел двигался так, чтобы координаты мыши оставались неподвижными относительно него , Эта версия выглядит так:

import javafx.fxml.FXML; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.canvas.GraphicsContext; 
import javafx.scene.input.MouseButton; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.StrokeLineCap; 
import javafx.scene.shape.StrokeLineJoin; 

public class CanvasDragController { 

    @FXML 
    private Pane rootPane; 

    @FXML 
    private Canvas canvas; 

    private double mouseX; 

    private double mouseY; 

    @FXML 
    private void initialize() { 
     GraphicsContext gc = canvas.getGraphicsContext2D(); 

     // Set the writing pen. 
     gc.setLineCap(StrokeLineCap.ROUND); 
     gc.setLineJoin(StrokeLineJoin.ROUND); 
     gc.setLineWidth(1); 
     gc.setStroke(Color.BLACK); 

     // Set the background to be transparent. 
     gc.setFill(Color.BLUE); 
     gc.fillRect(0, 0, 256, 256); 

     // Handle moving the canvas. 
     canvas.setOnMousePressed(e -> { 
      if (e.getButton() == MouseButton.PRIMARY) { 
       gc.moveTo(e.getX(), e.getY()); 
      } else if (e.getButton() == MouseButton.SECONDARY) { 
       mouseX = e.getX(); 
       mouseY = e.getY(); 
      } 
     }); 

     canvas.setOnMouseDragged(e -> { 
      if (e.getButton() == MouseButton.PRIMARY) { 
       gc.lineTo(e.getX(), e.getY()); 
       gc.stroke(); 
      } else if (e.getButton() == MouseButton.SECONDARY) { 
       double newMouseX = e.getX(); 
       double newMouseY = e.getY(); 
       double deltaX = newMouseX - mouseX; 
       double deltaY = newMouseY - mouseY; 
       canvas.setLayoutX(canvas.getLayoutX() + deltaX); 
       canvas.setLayoutY(canvas.getLayoutY() + deltaY); 
       // Why is this happening? 
       if (Math.abs(deltaX) > 4 || Math.abs(deltaY) > 4) 
        System.out.println(deltaX + " " + deltaY); 
      } 
     }); 
    } 
} 
Смежные вопросы