我最近开始学习javafx。在这里的几篇文章的帮助下,我创建了一个可拖动、可调整大小和可旋转的矩形。我的计划是将其用作自定义节点的调整大小控制。我希望旋转枢轴保持在矩形中心,但如果调整大小,中心会发生变化,当我设置新中心时,矩形会产生凹凸。为什么会发生这种情况以及我如何解决它?如果有任何错误,我很抱歉英语不是我的母语。预先感谢
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.stage.Stage;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
final Delta dragDelta = new Delta();
Wrapper<Point2D> mouseLocation = new Wrapper<>();
Pane root = new Pane();
Rectangle rect = new Rectangle(100,100);
final Rotate rotate = new Rotate();
{
//set pivot on rectangle center
rotate.setPivotX((rect.getX() + rect.getWidth())/2);
rotate.setPivotY((rect.getY() + rect.getHeight())/2);
}
rect.setStyle(
"-fx-stroke: blue; " +
"-fx-stroke-width: 2px; " +
"-fx-stroke-dash-array: 12 2 4 2; " +
"-fx-stroke-dash-offset: 6; " +
"-fx-stroke-line-cap: butt; " +
"-fx-fill: rgba(255, 255, 255, .0);"
);
Group group = new Group();
Circle rotateCircle = new Circle(7);
Circle topLeft = new Circle(7);
topLeft.setOnMousePressed(e->{
mouseLocation.value = new Point2D(e.getSceneX(), e.getSceneY());
});
topLeft.setOnMouseDragged(e->{
// Get the mouse deltas
double dx = e.getSceneX() - mouseLocation.value.getX();
double dy = e.getSceneY() - mouseLocation.value.getY();
dragDelta.x = e.getSceneX();
dragDelta.x = e.getSceneY();
// Get the angle in radians
double tau = - Math.toRadians(rotate.getAngle());
double sinTau = Math.sin(tau);
double cosTau = Math.cos(tau);
// Perform a rotation on dx and dy to the object coordinate
double dx_ = dx * cosTau - dy * sinTau;
double dy_ = dy * cosTau + dx * sinTau;
rect.setWidth(w(rect) - dx_);
rect.setHeight(h(rect) - dy_);
// Set save the current mouse value
mouseLocation.value = new Point2D(e.getSceneX(), e.getSceneY());
// Move the control
group.setTranslateX(group.getTranslateX() + dx);
group.setTranslateY(group.getTranslateY() + dy);
});
topLeft.setOnMouseReleased(e->{
//This is the problem
rotate.setPivotX((rect.getX() + rect.getWidth())/2);
rotate.setPivotY((rect.getY() + rect.getHeight())/2);
});
group.getTransforms().add(rotate);
rotateCircle.centerXProperty().bind(rect.xProperty().add(rect.widthProperty()).divide(2));
rotateCircle.centerYProperty().bind(rect.yProperty().subtract(25d));
topLeft.setCenterX(rect.getX());
topLeft.setCenterY(rect.getY());
group.getChildren().addAll(rect, rotateCircle, topLeft);
group.setLayoutX(100);
group.setLayoutY(100);
root.getChildren().add(group);
Util.enableDrag(rect, rotate);
Util.makeRotable(rotateCircle, rotate);
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public double w(Rectangle rect) {
return Math.abs(rect.getWidth() - rect.getX());
}
public double h(Rectangle rect) {
return Math.abs(rect.getHeight() - rect.getY());
}
// Return the angle from 0 to 360
public static double deltaAngle (double x, double y, double px, double py) {
double dx = x - px;
double dy = y - py;
double angle = Math.abs(Math.toDegrees(Math.atan2(dy, dx)));
if(dy < 0) {
angle = 360 - angle;
}
return angle;
}
static class Util {
static Wrapper<Point2D> mouseLocation = new Wrapper<>();
// make a targetNode movable by dragging it around with the mouse.
static void enableDrag(Rectangle rectangle, Rotate rotate) {
final var dragDelta = new Delta();
Parent parent = rectangle.getParent();
rectangle.setOnMousePressed(mouseEvent -> {
mouseLocation.value = new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY());
// record a delta distance for the drag and drop operation.
dragDelta.x = mouseEvent.getX() - mouseEvent.getX();
dragDelta.y = mouseEvent.getY() - mouseEvent.getY();
parent.getScene().setCursor(Cursor.MOVE);
});
rectangle.setOnMouseReleased(mouseEvent -> {
mouseLocation.value = null ;
parent.getScene().setCursor(Cursor.HAND);
parent.relocate(parent.getLayoutX() + parent.getTranslateX(),
parent.getLayoutY() + parent.getTranslateY());
parent.setTranslateX(0);
parent.setTranslateY(0);
rotate.setPivotX((rectangle.getX() + rectangle.getWidth())/2);
rotate.setPivotY((rectangle.getY() + rectangle.getHeight())/2);
});
rectangle.setOnMouseDragged(mouseEvent -> {
// Get the mouse deltas
double deltaX = mouseEvent.getSceneX() - mouseLocation.value.getX();
double deltaY = mouseEvent.getSceneY() - mouseLocation.value.getY();
parent.setTranslateX(parent.getTranslateX() + deltaX);
parent.setTranslateY(parent.getTranslateY() + deltaY);
mouseLocation.value = new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY());
});
}
// make a targetNode movable by dragging it around with the mouse.
static void makeRotable(Circle circle, Rotate rotate) {
final var dragDelta = new Delta();
// Make it draggable
circle.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
dragDelta.x = event.getSceneX();
dragDelta.x = event.getSceneY();
});
// When it's dragged rotate the box
circle.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> {
var localToScene = circle.getParent().getLocalToSceneTransform();
double x1 = dragDelta.x;
double y1 = dragDelta.y;
var x2 = event.getSceneX();
var y2 = event.getSceneY();
var px = rotate.getPivotX() + localToScene.getTx();
var py = rotate.getPivotY() + localToScene.getTy();
// Work out the angle rotated
double th1 = deltaAngle(x1, y1, px, py);
double th2 = deltaAngle(x2, y2, px, py);
var angle = rotate.getAngle();
angle += th2 - th1;
// Rotate the rectangle
rotate.setAngle(angle);
dragDelta.x = event.getSceneX();
dragDelta.y = event.getSceneY();
});
}
}
// records relative x and y co-ordinates.
private static class Delta {
double x, y;
}
static class Wrapper<T> {
T value ;
}
public static void main(String[] args) {
launch(args);
}
}
您通过设置新的宽度和高度来缩放矩形:
rect.setWidth(w(rect) - dx_);
rect.setHeight(h(rect) - dy_);
这保持了矩形的原点,但改变了它的中心。
相反,请考虑缩放围绕其中心的矩形:
转型。有了这个,您可以指定“枢轴”点 发生规模。Scale
如本 so 答案中所解释和演示。
通过使用 Scale.setX 和 Scale.setY 您可以应用不同的宽度和高度比例。
编辑:
当更改
Rotate
的枢轴时,它也会影响更改之前执行的旋转。这就是“跳”的原因
你看。
为了演示它,请运行以下 mre,其中一个按钮执行旋转,另一个按钮仅更改枢轴:import javafx.application.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100,100);
rect.setStyle(
"-fx-stroke: blue; " +
"-fx-fill: rgba(255, 255, 255, .0);"
);
rect.setLayoutX(100);
rect.setLayoutY(100);
Rotate rotate = new Rotate();
rotate.setPivotX((rect.getX() + rect.getWidth())/2);
rotate.setPivotY((rect.getY() + rect.getHeight())/2);
rect.getTransforms().add(rotate);
Button rotateBtn = new Button("Rotate");
Button movePivotBtn = new Button("Move Rotate Pivot");
movePivotBtn.setDisable(true);
movePivotBtn.setLayoutX(60);
rotateBtn.setOnAction(e->{
rotate.setAngle(45);
rotateBtn.setDisable(true);
movePivotBtn.setDisable(false);
});
movePivotBtn.setOnAction(e->{
rotate.setPivotX(rotate.getPivotX()+15);
movePivotBtn.setDisable(true);
});
Pane root = new Pane(rotateBtn, movePivotBtn, rect);
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
谢谢。如何避免这种“跳跃”?