JavaFX - Zoom relative to mouse position

Deadly 提交于 2019-12-06 10:37:06

I had the same problem and I did the following - I wouldn't call is a proper solution (rather a workaround), but it works mostly fine.

Basically, I do some calculations to retrieve the correct values of Hvalue and Vvalue. Moreover, I have to apply a compensation factor due to the fact that the ScrollPane will do not only the zooming but also a vertical translation of the image (due to standard behaviour of the ScrollPane).

Here you find the code.

/**
 * Pane containing an image that can be resized.
 */
public class SizeableImageView extends ScrollPane {
    /**
     * The zoom factor to be applied for each zoom event.
     *
     * (480th root of 2 means that 12 wheel turns of 40 will result in size factor 2.)
     */
    private static final double ZOOM_FACTOR = 1.0014450997779993488675056142818;

    /**
     * The zoom factor.
     */
    private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1000);

    /**
     * The mouse X position.
     */
    private final DoubleProperty mouseXProperty = new SimpleDoubleProperty();

    /**
     * The mouse Y position.
     */
    private final DoubleProperty mouseYProperty = new SimpleDoubleProperty();

    /**
     * Constructor without initialization of image.
     */
    public SizeableImageView() {
        this(new ImageView());
    }

    /**
     * Constructor, initializing with an image view.
     *
     * @param imageView
     *            The ImageView to be displayed.
     */
    public SizeableImageView(final ImageView imageView) {
        setContent(imageView);

        setPannable(true);
        setHbarPolicy(ScrollBarPolicy.NEVER);
        setVbarPolicy(ScrollBarPolicy.NEVER);

        setOnMouseMoved(new EventHandler<MouseEvent>() {
            @Override
            public void handle(final MouseEvent event) {
                mouseXProperty.set(event.getX());
                mouseYProperty.set(event.getY());
            }
        });

        addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
            @Override
            public void handle(final ScrollEvent event) {
                ImageView image = (ImageView) getContent();

                // Original size of the image.
                double sourceWidth = zoomProperty.get() * image.getImage().getWidth();
                double sourceHeight = zoomProperty.get() * image.getImage().getHeight();

                zoomProperty.set(zoomProperty.get() * Math.pow(ZOOM_FACTOR, event.getDeltaY()));

                // Old values of the scrollbars.
                double oldHvalue = getHvalue();
                double oldVvalue = getVvalue();

                // Image pixels outside the visible area which need to be scrolled.
                double preScrollXFactor = Math.max(0, sourceWidth - getWidth());
                double preScrollYFactor = Math.max(0, sourceHeight - getHeight());

                // Relative position of the mouse in the image.
                double mouseXPosition = (mouseXProperty.get() + preScrollXFactor * oldHvalue) / sourceWidth;
                double mouseYPosition = (mouseYProperty.get() + preScrollYFactor * oldVvalue) / sourceHeight;

                // Target size of the image.
                double targetWidth = zoomProperty.get() * image.getImage().getWidth();
                double targetHeight = zoomProperty.get() * image.getImage().getHeight();

                // Image pixels outside the visible area which need to be scrolled.
                double postScrollXFactor = Math.max(0, targetWidth - getWidth());
                double postScrollYFactor = Math.max(0, targetHeight - getHeight());

                // Correction applied to compensate the vertical scrolling done by ScrollPane
                double verticalCorrection = (postScrollYFactor / sourceHeight) * event.getDeltaY();

                // New scrollbar positions keeping the mouse position.
                double newHvalue = ((mouseXPosition * targetWidth) - mouseXProperty.get()) / postScrollXFactor;
                double newVvalue =
                        ((mouseYPosition * targetHeight) - mouseYProperty.get() + verticalCorrection)
                                / postScrollYFactor;

                image.setFitWidth(targetWidth);
                image.setFitHeight(targetHeight);

                setHvalue(newHvalue);
                setVvalue(newVvalue);
            }
        });

        addEventFilter(ZoomEvent.ANY, new EventHandler<ZoomEvent>() {
            @Override
            public void handle(final ZoomEvent event) {
                zoomProperty.set(zoomProperty.get() * event.getZoomFactor());

                ImageView image = (ImageView) getContent();
                image.setFitWidth(zoomProperty.get() * image.getImage().getWidth());
                image.setFitHeight(zoomProperty.get() * image.getImage().getHeight());
            }
        });

    }

    /**
     * Set the image view displayed by this class.
     *
     * @param imageView
     *            The ImageView.
     */
    public final void setImageView(final ImageView imageView) {
        setContent(imageView);
        zoomProperty.set(Math.min(imageView.getFitWidth() / imageView.getImage().getWidth(), imageView.getFitHeight()
                / imageView.getImage().getHeight()));
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!