简体   繁体   English

JavaFx 8 - 相对于鼠标位置缩放/缩放ScrollPane

[英]JavaFx 8 - Scaling / zooming ScrollPane relative to mouse position

I need to zoom in / out on a scroll pane, relative to the mouse position. 我需要相对于鼠标位置放大/缩小滚动窗格。

I currently achieve the zooming functionality by wrapping my content in a Group, and scaling the group itself. 我目前通过将我的内容包装在一个组中,并缩放组本身来实现缩放功能。 I create a new Scale object with a custom pivot. 我使用自定义数据透视创建一个新的Scale对象。 (Pivot is set to the mouse position) (枢轴设置为鼠标位置)

This works perfectly for where the Group's initial scale is 1.0, however scaling afterwards does not scale in the correct direction - I believe this is because the relative mouse position changes when the Group has been scaled. 这适用于组的初始比例为1.0的情况,但之后的缩放不会在正确的方向上缩放 - 我相信这是因为当缩放组时相对鼠标位置会发生变化。

My code: 我的代码:

@Override
public void initialize(URL location, ResourceBundle resources) {

    Delta initial_mouse_pos = new Delta();

    anchorpane.setOnScrollStarted(event -> {
        initial_mouse_pos.x = event.getX();
        initial_mouse_pos.y = event.getY();
    });

    anchorpane.setOnScroll(event -> {
        double zoom_fac = 1.05;
        double delta_y = event.getDeltaY();

        if(delta_y < 0) {
            zoom_fac = 2.0 - zoom_fac;
        }

        Scale newScale = new Scale();
        newScale.setPivotX(initial_mouse_pos.x);
        newScale.setPivotY(initial_mouse_pos.y);
        newScale.setX( content_group.getScaleX() * zoom_fac );
        newScale.setY( content_group.getScaleY() * zoom_fac );

        content_group.getTransforms().add(newScale);

        event.consume();
    });
}

private class Delta { double x, y; }

How do I get the correct mouse position at different levels of scaling? 如何在不同的缩放级别获得正确的鼠标位置? Is there a completely different way to zooming the ScrollPane that is easier? 是否有一种完全不同的方式来缩放ScrollPane更容易?

This is a scalable, pannable JavaFX ScrollPane : 这是一个可扩展的,可上传的 JavaFX ScrollPane:

import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;

public class ZoomableScrollPane extends ScrollPane {
    private double scaleValue = 0.7;
    private double zoomIntensity = 0.02;
    private Node target;
    private Node zoomNode;

    public ZoomableScrollPane(Node target) {
        super();
        this.target = target;
        this.zoomNode = new Group(target);
        setContent(outerNode(zoomNode));

        setPannable(true);
        setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        setFitToHeight(true); //center
        setFitToWidth(true); //center

        updateScale();
    }

    private Node outerNode(Node node) {
        Node outerNode = centeredNode(node);
        outerNode.setOnScroll(e -> {
            e.consume();
            onScroll(e.getTextDeltaY(), new Point2D(e.getX(), e.getY()));
        });
        return outerNode;
    }

    private Node centeredNode(Node node) {
        VBox vBox = new VBox(node);
        vBox.setAlignment(Pos.CENTER);
        return vBox;
    }

    private void updateScale() {
        target.setScaleX(scaleValue);
        target.setScaleY(scaleValue);
    }

    private void onScroll(double wheelDelta, Point2D mousePoint) {
        double zoomFactor = Math.exp(wheelDelta * zoomIntensity);

        Bounds innerBounds = zoomNode.getLayoutBounds();
        Bounds viewportBounds = getViewportBounds();

        // calculate pixel offsets from [0, 1] range
        double valX = this.getHvalue() * (innerBounds.getWidth() - viewportBounds.getWidth());
        double valY = this.getVvalue() * (innerBounds.getHeight() - viewportBounds.getHeight());

        scaleValue = scaleValue * zoomFactor;
        updateScale();
        this.layout(); // refresh ScrollPane scroll positions & target bounds

        // convert target coordinates to zoomTarget coordinates
        Point2D posInZoomTarget = target.parentToLocal(zoomNode.parentToLocal(mousePoint));

        // calculate adjustment of scroll position (pixels)
        Point2D adjustment = target.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));

        // convert back to [0, 1] range
        // (too large/small values are automatically corrected by ScrollPane)
        Bounds updatedInnerBounds = zoomNode.getBoundsInLocal();
        this.setHvalue((valX + adjustment.getX()) / (updatedInnerBounds.getWidth() - viewportBounds.getWidth()));
        this.setVvalue((valY + adjustment.getY()) / (updatedInnerBounds.getHeight() - viewportBounds.getHeight()));
    }
}

Did you try to remove the setOnScrollStarted -event and move its content to the setOnScroll -event? 您是否尝试删除setOnScrollStarted -event并将其内容移至setOnScroll -event?

Doing so reduces the need of your extra Delta -class and the computations of your mouse-positions are always on par with the current zoom factor. 这样做可以减少额外Delta级的需求,并且鼠标位置的计算总是与当前缩放系数相同。

I implemented the same thing and it works the way you are describing it. 我实现了相同的东西,它的工作方式与描述方式相同。

Somehow like this: 不知何故这样:

@Override
public void initialize(URL location, ResourceBundle resources) {

    anchorpane.setOnScroll(event -> {
        double zoom_fac = 1.05;

        if(delta_y < 0) {
            zoom_fac = 2.0 - zoom_fac;
        }

        Scale newScale = new Scale();
        newScale.setPivotX(event.getX);
        newScale.setPivotY(event.getY);
        newScale.setX( content_group.getScaleX() * zoom_fac );
        newScale.setY( content_group.getScaleY() * zoom_fac );

        content_group.getTransforms().add(newScale);

        event.consume();
    });
}

I believe this is a duplicate of this question which involves the same concepts at work. 我认为这是这个问题的重复,涉及工作中的相同概念。 If you don't really care if it zooms relative to your mouse and just prefer it zoom in the center look at this question. 如果你真的不在乎它是否相对于你的鼠标变焦,只是更喜欢它放大中心看这个问题。 If you need any more help comment below. 如果您在下面需要更多帮助评论。

Assuming you want to have the following zoom behavior: When the mouse wheel is pushed forward/backward the object under the cursor will be scaled up/down and the area under the cursor is now centered within the zooming area. 假设您想要具有以下缩放行为:当向前/向后推动鼠标滚轮时,光标下的对象将按比例放大/缩小,光标下的区域现在在缩放区域中居中。 Eg. 例如。 pushing the wheel forward while pointing at a place left from the center of the zooming area results in a ' up-scale and move right ' action. 在指向缩放区域中心左侧的位置时向前推动车轮会导致“ 向上移动并向右移动 ”动作。

The scaling thing is as simple as you have already done so far. 扩展的事情就像你到目前为止一样简单。 The tricky part is the move action. 棘手的部分是移动动作。 There are some problem you have to consider within your calculations: 在计算中你必须考虑一些问题:

  1. You have to calculate the difference from the center and the position of the cursor. 您必须计算与光标中心和位置的差异。 You can calculate this value by subtracting the center point (cp) from the mouse position (mp). 您可以通过从鼠标位置(mp)减去中心点(cp)来计算此值。

  2. If your zoom level is 1 and you point 50px left from the center you want to move your object 50px to the right, because 1px of your screen corresponds to one 1px of your object (picture). 如果您的缩放级别为1并且您从中心向左指向50px,则您希望将对象向右移动50px,因为屏幕的1px对应于对象的1px(图片)。 But if you doubled the size of your object, than 2 screen pixel are equal to on object pixel. 但是,如果您将对象的大小加倍,则2个屏幕像素等于对象像素。 You have to consider this when moving the object, because the translating part is always done before the scaling part. 移动对象时必须考虑这一点,因为翻译部分总是在缩放部分之前完成。 In other words you are moving your object in original size and the scaling is only the second independent step. 换句话说,您正在以原始大小移动对象,缩放仅是第二个独立步骤。

  3. How is the scaling done? 缩放是如何完成的? In JavaFX you simply set some scale-properties and JavaFX does the rest for you. 在JavaFX中,您只需设置一些scale-properties,JavaFX将为您完成剩下的工作。 But what does it exactly? 但究竟是什么呢? The important thing to know is, where the fixed point is while zooming the object. 重要的是要知道,固定点是在缩放对象时的位置。 If you scale an object there will be one point which stays fixed at its position while all other points are moving towards this point or moving way from it. 如果缩放对象,则会有一个点固定在其位置,而所有其他点都朝向此点移动或从其移动。 As the documentation of JavaFX says the center of the zoomed object will be the fixed point. 正如JavaFX的文档所说,缩放对象的中心将是固定点。

Defines the factor by which coordinates are scaled about the center of the object along the X axis of this Node. 定义坐标沿此节点的X轴围绕对象中心缩放的因子。

That means you have to ensure that your visual center point is equal to the one JavaFX uses while scaling you object. 这意味着您必须确保您的视觉中心点等于JavaFX在缩放对象时使用的点。 You can achieve this if you wrap your object within a container. 如果将对象包装在容器中,则可以实现此目的。 Now zoom the container instead of the object and position the object within the container to fit your needs. 现在缩放容器而不是对象,并将对象放在容器中以满足您的需要。

I hope this helps. 我希望这有帮助。 If you need more help please offer a short working example project. 如果您需要更多帮助,请提供简短的工作示例项目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM