拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 拖动并放大或缩小影像后如何重新居中

拖动并放大或缩小影像后如何重新居中

白鹭 - 2022-02-11 2141 0 0

我正在尝试创建一个处理缩放和平移组件的类。该类包含对我正在使用的 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 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *