2015-09-20 21 views
1

我不明白爲什麼會發生這種情況。目前我正在嘗試通過右鍵單擊節點(畫布),按住鼠標右鍵並移動鼠標。起初它很流暢,但隨後開始出現這種奇怪的抖動。這隻發生在我按住鼠標按鈕的時候。如果你釋放它,那麼它會恢復正常(但可以很快再次發生)。JavaFX MouseEvent定位精度隨時間降低,導致節點移動抖動

通過一些調試,看起來您移動得越多,您在鼠標位置越來越不同步的每個拖動事件之間獲得「不精確」。最終的結果是它來回翻轉,看起來很糟糕。

代碼被使用:

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。由於您隨後移動畫布,舊的座標現在不正確(因爲它們相對於舊位置,而不是新位置)。因此,您的deltaXdeltaY的值不正確。

要解決這個問題,只需相對於「固定」的東西進行測量即可。 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); 
      } 
     }); 
    } 
} 
相關問題