JavaFX MouseeEvent的定位精度会随着时间的推移而降低,导致节点移动抖动
我不明白为什么会这样。目前,我正试图通过右键单击、按住右键单击并移动鼠标来拖动节点(画布)。起初它是平滑的,但后来它开始有这种奇怪的抖动。这只在我按住鼠标按钮时发生。如果你释放它,它就会恢复正常(但很快又会变得紧张) 通过一些调试,您移动它的次数越多,在鼠标位置越来越不同步的每个拖动事件之间,您就会获得这种“不精确性”。最终的结果是它会前后摇摆,这看起来很糟糕 正在使用的代码: Main.javaJavaFX MouseeEvent的定位精度会随着时间的推移而降低,导致节点移动抖动,java,canvas,javafx,mouse,Java,Canvas,Javafx,Mouse,我不明白为什么会这样。目前,我正试图通过右键单击、按住右键单击并移动鼠标来拖动节点(画布)。起初它是平滑的,但后来它开始有这种奇怪的抖动。这只在我按住鼠标按钮时发生。如果你释放它,它就会恢复正常(但很快又会变得紧张) 通过一些调试,您移动它的次数越多,在鼠标位置越来越不同步的每个拖动事件之间,您就会获得这种“不精确性”。最终的结果是它会前后摇摆,这看起来很糟糕 正在使用的代码: Main.java import javafx.application.Application; import jav
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);
}
}
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);
}
});
}
}
Controller.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);
}
}
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像素),即使你只移动了一个像素
然后,它似乎试图在你下次移动时修复自己,这给了它一个丑陋的神经过敏的外观
我感到困惑的原因是,有时它工作得很好,但当你继续拖动时,精确度就会下降
我是否编写了错误的代码,或者这是一个潜在的错误?使用
MouseEvent.getX()
和MouseEvent.getY()
测量的坐标与事件发生的节点相关:即相对于画布。由于随后移动画布,旧坐标现在不正确(因为它们是相对于旧位置的,而不是相对于新位置的)。因此,deltaX
和deltaY
的值不正确
要解决此问题,只需相对于“固定”的对象进行测量,例如场景
。您可以使用MouseEvent.getSceneX()
和MouseEvent.getSceneY()
来实现这一点
另一种方法(至少对我来说不是很直观)是使用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);
}
});
}
}