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

时间:2016-10-03 09:02:00

标签: java javafx javafx-8 zoom scrollpane

我需要相对于鼠标位置在滚动窗格上放大/缩小。

我目前通过将内容包装在一个组中并扩展组本身来实现缩放功能。我使用自定义数据透视创建一个新的Scale对象。 (枢轴设置为鼠标位置)

这适用于Group的初始比例为1.0的情况,但之后的缩放不会按正确方向缩放 - 我相信这是因为当缩放组时相对鼠标位置会发生变化。

我的代码:

@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; }

如何在不同的缩放级别获得正确的鼠标位置?是否有一种完全不同的方式来缩放ScrollPane更容易?

4 个答案:

答案 0 :(得分:12)

这是一个可扩展,可扩展的 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()));
    }
}

答案 1 :(得分:1)

您是否尝试删除 setOnScrollStarted -event并将其内容移至 setOnScroll -event?

这样做可以减少额外 Delta 类的需要,并且鼠标位置的计算总是与当前缩放系数相同。

我实现了同样的事情,它的工作方式与你描述的方式相同。

不知怎的这样:

@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();
    });
}

答案 2 :(得分:0)

我认为这是this问题的重复,涉及工作中的相同概念。如果你真的不在乎它是否相对于你的鼠标进行缩放而只是更喜欢它放大中心看this问题。如果您在下面需要更多帮助评论。

答案 3 :(得分:0)

假设您想要具有以下缩放行为: 当向前/向后按下鼠标滚轮时,光标下方的对象将按比例放大/缩小,光标下方的区域现在在缩放区域内居中。 例如。在指向缩放区域中心左侧的位置时向前推动滚轮会导致“向上扩展并向右移动”动作。

缩放的事情就像你到目前为止一样简单。 棘手的部分是移动动作。在计算中你必须考虑一些问题:

  1. 你必须计算与中心和中心的差异 光标的位置。您可以通过减去来计算此值 从鼠标位置(mp)开始的中心点(cp)。

  2. 如果您的缩放级别为1并且您从中心向左指向50px,则您想要将对象向右移动50px,因为1px的屏幕对应于对象的1px(图片)。但是,如果您将对象的大小加倍,则2个屏幕像素等于对象像素。移动对象时必须考虑这一点,因为翻译部分总是在缩放部分之前完成。换句话说,您正在以原始大小移动对象,缩放仅是第二个独立步骤。

  3. 如何进行缩放?在JavaFX中,您只需设置一些scale-properties,JavaFX将为您完成剩下的工作。但究竟是什么呢?重要的是要知道,固定点是在缩放对象时的位置。如果缩放对象,则会有一个点固定在其位置,而所有其他点都朝向此点移动或从其移动。 正如JavaFX的文档所说,缩放对象的中心将是固定点。

  4.   

    定义坐标围绕中心缩放的因子   沿着这个节点的X轴的对象。

    这意味着您必须确保您的视觉中心点等于JavaFX在缩放对象时使用的点。如果将对象包装在容器中,则可以实现此目的。现在缩放容器而不是对象,并将对象放在容器中以满足您的需要。

    我希望这会有所帮助。如果您需要更多帮助,请提供一个简短的工作示例项目。