我正在尝试创建一个处理缩放和平移组件的类。该类包含对我正在使用的 Anchorpane 的参考,因为它默认情况下不进行任何对齐,以及我希望能够拖动和缩放的 ImageView。缩放和平移都单独作业,但是一旦放大或缩小影像,我就无法重新居中。
这个想法是我希望能够在应用程序调整大小时使影像居中,以便拖动总是相对于 Anchorpane 的中心。默认情况下,Anchorpane 中的任何位移都是相对于左上角的,但这对用户来说并不直观。内容看起来相对于中心移动会更合乎逻辑。为了实作这一点,我们的想法是在视窗大小改变时重新定位内容,然后应用与用户所做的拖动量相对应的翻译
如果您运行我发布的代码并放大,然后调整视窗大小,您会注意到代表影像的红色矩形到处都是。如果你缩小视窗,那么正方形将不再和以前一样在同一个地方。这仅在应用了缩放并且似乎是重新居中方法的问题时才会发生。
如果居中方法正常作业,则正方形应回传到视窗展开前的相同位置,当正方形处于 1 比 1 比例且未发生缩放时会发生这种情况
这是我用来处理缩放和拖动的类
package com.example.test;
import javafx.beans.value.ChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
public class ZoomController{
// the plane on which the node is being dragged
private final AnchorPane PLANE;
// the node I want to zoom and drag
private final Node CONTENT;
private Point2D lastContentPosition = new Point2D(0, 0);
private Point2D lastMousePosition = new Point2D(0, 0);
// the total amount of dragging
// applied to the CONTENT node
private Point2D dragOffset = new Point2D(0, 0);
// the total amount of scaling
// applied to the CONTENT node
private int scale = 1;
public ZoomController(
AnchorPane plane,
Node content
) {
this.PLANE = plane;
this.CONTENT = content;
addListeners();
// artificially reproduces the problem
applyZoom(2, new Point2D(0, 350));
applyDrag(new Point2D(-100, 0));
}
private void addListeners() {
// tries to center the CONTENT whenever the window is resized
PLANE.heightProperty().addListener(centerContent());
PLANE.widthProperty().addListener(centerContent());
// saves the mouse's position whenever the mouse moves
PLANE.setOnMousePressed(getMousePosition());
PLANE.setOnMouseDragged(drag());
PLANE.setOnScroll(handleScroll());
}
private ChangeListener<Number> centerContent() {
return (observableValue, number, t1) -> {
centerView();
};
}
private EventHandler<MouseEvent> drag() {
return mouseEvent -> {
// calculates the path taken by the mouse...
Point2D newMousePosition = new Point2D(mouseEvent.getX(), mouseEvent.getY());
Point2D mouseTranslation = lastMousePosition.subtract(newMousePosition);
// ...and saves its new position
updateMousePosition(mouseEvent);
// applies the drag
applyDrag(mouseTranslation);
};
}
private EventHandler<MouseEvent> getMousePosition() {
return this::updateMousePosition;
}
private EventHandler<ScrollEvent> handleScroll() {
return scrollEvent -> {
// filters out the mouse stopping to scroll
if (scrollEvent.getDeltaX() == 0 && scrollEvent.getDeltaY() == 0) return;
// starts zooming
if (scrollEvent.isControlDown()) {
zoom(scrollEvent);
}
};
}
private void zoom(ScrollEvent scrollEvent) {
// adds or subtracts to the image's scale based on
// whether user is scrolling backwards or forwards
final double dScale = scrollEvent.getDeltaY() > 0 ? 0.1 : -0.1;
scale = dScale;
// gets the coordinates IN THE IMAGE's FRAME OF REFERENCE
// of the point at which to zoom the image so it is centered on the mouse
Point2D target = CONTENT.parentToLocal(new Point2D(scrollEvent.getX(), scrollEvent.getY()));
// applies the zoom to the image
applyZoom(1 dScale, target);
// saves the image's position once it has been zoomed
updateContentPosition();
}
private void applyZoom(final double zoomAmount, Point2D target) {
// applies the necessary scaling to the image...
Scale zoom = new Scale(zoomAmount, zoomAmount);
// ...and centers the scaling to the point where the mouse is located at
zoom.setPivotY(target.getY());
zoom.setPivotX(target.getX());
CONTENT.getTransforms().add(zoom);
updateContentPosition();
}
private void applyDrag(Point2D dragAmount) {
// drag amount always corresponds to the mouse's displacement
// for the moment this is a 1 to 1 mapping
// since I have not figured out how to take the scale into consideration
// updates the total displacement caused by drag (used when we re-center the image)
dragOffset = dragOffset.subtract(dragAmount);
// applies the necessary translation to the image...
Translate drag = new Translate();
// ...based on the mouse's movement
drag.setX(-dragAmount.getX());
drag.setY(-dragAmount.getY());
CONTENT.getTransforms().add(drag);
// saves the image's position after it has been dragged
updateContentPosition();
}
private void centerView() {
// gets the coordinates we need to place the image at for it to be centered
Point2D centerPosition = getCenterEdge();
// calculates the path to take from the image's current position
// to the position it has to be at to be centered
// ie: the displacement vector
Point2D translation = centerPosition.subtract(lastContentPosition);
// applies the necessary translation to the image...
Translate translateToCenter = new Translate();
// ...while account for drag so image is not fully re-centered
translateToCenter.setX(translation.getX() dragOffset.getX());
translateToCenter.setY(translation.getY() dragOffset.getY());
CONTENT.getTransforms().add(translateToCenter);
// saves the image's position after it has been centered
updateContentPosition();
}
private void updateMousePosition(MouseEvent mouseEvent) {
lastMousePosition = new Point2D(mouseEvent.getX(), mouseEvent.getY());
}
private void updateContentPosition() {
// updates the image's position
lastContentPosition = getContentPosition();
}
private Point2D getContentPosition() {
// gets the minimal coordinates of the bounds around the image
// ie: the image's coordinates
Bounds contentBounds = CONTENT.getBoundsInParent();
return new Point2D(contentBounds.getMinX(), contentBounds.getMinY());
}
private Point2D getCenterEdge() {
// gets the size of the image and the anchor pane it is in...
Point2D contentSize = getContentSize();
Point2D availableSpace = getAvailableSpace();
// ...to determine the coordinates at which to place the image for it to be centerd
return new Point2D(
(availableSpace.getX() - contentSize.getX()) / 2,
(availableSpace.getY() - contentSize.getY()) / 2
);
}
private Point2D getContentSize() {
// gets the bounds around the image
Bounds contentBounds = CONTENT.getBoundsInParent();
return new Point2D(contentBounds.getWidth(), contentBounds.getHeight());
}
private Point2D getAvailableSpace() {
// gets the size of the Anchorpane the image is inn
return new Point2D(PLANE.getWidth(), PLANE.getHeight());
}
}
这是我用来测验缩放的主要课程
package com.example.test;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ZoomMain extends Application {
@Override
public void start(Stage stage) throws Exception {
// the node we want to drag & zoom
Rectangle rectangle = new Rectangle(200, 100);
rectangle.setFill(Color.RED);
// the plane on which the node is being dragged
AnchorPane plane = new AnchorPane();
// adds the node
Scene mainScene = new Scene(plane);
plane.getChildren().add(rectangle);
// handles the zoom
ZoomController zoomController = new ZoomController(
plane,
rectangle
);
stage.setTitle("Zooming test");
stage.setScene(mainScene);
stage.setMinHeight(500);
stage.setMinWidth(500);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
我已经尝试将影像分组以拖动和缩放到一个组中,这将很有用,因为它可以让我一次包含多个要拖动的节点,但是当我这样做时,组的内容仍然不可见。
uj5u.com热心网友回复:
不是在缩放后重新居中内容,而是使用内容的中心点作为缩放枢轴。换句话说,围绕内容的中心进行缩放。
介绍一个简单的计算中心的方法:
private Point2D getContentCenter() {
Bounds contentBounds = CONTENT.getBoundsInLocal();
return new Point2D(contentBounds.getCenterX(), contentBounds.getCenterY());
}
并围绕它进行缩放:
private void zoom(ScrollEvent scrollEvent) {
// adds or subtracts to the image's scale based on
// whether user is scrolling backwards or forwards
final double dScale = scrollEvent.getDeltaY() > 0 ? 0.1 : -0.1;
scale = dScale;
Point2D target = getContentCenter();
// applies the zoom to the image
applyZoom(1 dScale, target);
}
编辑:以下ZoomController
实作了在调整窗格大小时围绕嘴巴位置缩放和内容中心化:
class ZoomController{
// the plane on which the node is being dragged
private final AnchorPane PLANE;
// the node I want to zoom and drag
private final Node CONTENT;
// the total amount of scaling applied to the CONTENT node
private int scale = 1;
private Point2D lastMousePosition = new Point2D(0, 0);
public ZoomController( AnchorPane plane, Node content ) {
PLANE = plane;
CONTENT = content;
PLANE.heightProperty().addListener((obs, number, t1) -> centerView());
PLANE.widthProperty().addListener((obs, number, t1) -> centerView());
// saves the mouse's position whenever the mouse moves
PLANE.setOnMousePressed(event -> updateMousePosition(event));
PLANE.setOnMouseDragged(drag());
PLANE.setOnScroll(handleScroll());
}
private void updateMousePosition(MouseEvent mouseEvent) {
lastMousePosition = new Point2D(mouseEvent.getX(), mouseEvent.getY());
}
private EventHandler<ScrollEvent> handleScroll() {
return scrollEvent -> {
// filters out the mouse stopping to scroll
if (scrollEvent.getDeltaX() == 0 && scrollEvent.getDeltaY() == 0) return;
// starts zooming
if (scrollEvent.isControlDown()) {
zoom(scrollEvent);
}
};
}
private void zoom(ScrollEvent scrollEvent) {
// adds or subtracts to the image's scale based on
// whether user is scrolling backwards or forwards
final double dScale = scrollEvent.getDeltaY() > 0 ? 0.1 : -0.1;
scale = dScale;
// scale around mouse position
Point2D pivot = CONTENT.parentToLocal(new Point2D(scrollEvent.getX(), scrollEvent.getY()));
//applies the zoom to the image
applyZoom(1 dScale, pivot);
}
private void applyZoom(final double zoomAmount, Point2D target) {
// applies the necessary scaling to the image...
Scale zoom = new Scale(zoomAmount, zoomAmount);
// ...and centers the scaling to the point where the mouse is located at
zoom.setPivotY(target.getY());
zoom.setPivotX(target.getX());
CONTENT.getTransforms().add(zoom);
}
private EventHandler<MouseEvent> drag() {
return mouseEvent -> {
// calculates the path taken by the mouse...
Point2D newMousePosition = new Point2D(mouseEvent.getX(), mouseEvent.getY());
Point2D mouseTranslation = lastMousePosition.subtract(newMousePosition);
// ...and saves its new position
updateMousePosition(mouseEvent);
// applies the drag
applyDrag(mouseTranslation);
};
}
private void applyDrag(Point2D dragAmount) {
// applies the necessary translation to the image...
Translate drag = new Translate();
// ...based on the mouse's movement
drag.setX(-dragAmount.getX());
drag.setY(-dragAmount.getY());
CONTENT.getTransforms().add(drag);
}
private void centerView() {
// gets the coordinates we need to place the image at for it to be centered
Point2D centerPosition = CONTENT.parentToLocal(getCenterEdge());
Point2D position = getContentPosition();
Point2D translation = centerPosition.subtract(position);
// applies the necessary translation to the image...
Translate translateToCenter = new Translate();
translateToCenter.setX(translation.getX());
translateToCenter.setY(translation.getY());
CONTENT.getTransforms().add(translateToCenter);
}
private Point2D getContentPosition() {
// gets the minimal coordinates of the bounds around the image
// ie: the image's coordinates
Bounds contentBounds = CONTENT.getBoundsInLocal();
return new Point2D(contentBounds.getMinX(), contentBounds.getMinY());
}
private Point2D getCenterEdge() {
// gets the size of the image and the anchor pane it is in...
Point2D contentSize = getContentSize();
Point2D parentSize = getParentSize();
// ...to determine the coordinates at which to place the image for it to be centered
return new Point2D(
(parentSize.getX() - contentSize.getX()) / 2,
(parentSize.getY() - contentSize.getY()) / 2
);
}
private Point2D getContentSize() {
//BoundsInParent: rectangular bounds of this Node which include its transforms
Bounds contentBounds = CONTENT.getBoundsInParent();
return new Point2D(contentBounds.getWidth(), contentBounds.getHeight());
}
private Point2D getParentSize() {//renamed from getAvailableSpace()
// gets the size of the Anchorpane the image is inn
return new Point2D(PLANE.getWidth(), PLANE.getHeight());
}
}
0 评论