【问题标题】:JavaFX - Zoom relative to mouse positionJavaFX - 相对于鼠标位置缩放
【发布时间】:2014-04-22 15:00:59
【问题描述】:

我无法在 JavaFX 中获得相对于鼠标位置的工作缩放。我已经阅读并尝试重写thisthis,但它不起作用。无法使用 scrollPane.setViewportBounds() 滚动视口。我必须使用scrollPane.setHvalue()scrollPane.setVvalue(),但无法正确重新计算以保持鼠标光标下的相同像素。当前代码保持滚动条位置:

ZoomHandler.java

import javafx.event.EventHandler;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;

public class ZoomHandler implements EventHandler<ScrollEvent> {

    private final ScrollPane scrollPane;
    private final Pane canvas;    
    private final double minScale;
    private final double maxScale;

    public ZoomHandler(ScrollPane scrollPane, Pane canvas, double minScale, double maxScale) {
        this.scrollPane = scrollPane;
        this.canvas = canvas;
        this.minScale = minScale;
        this.maxScale = maxScale;
    }

    public ZoomHandler(ScrollPane scrollPane, Pane canvas) {
        this(scrollPane, canvas, 0.1, 10);
    }

    @Override
    public void handle(ScrollEvent e) {
        if (e.isControlDown()) {
            double actualScale = canvas.getScaleX();

            if (actualScale > maxScale || actualScale < minScale) {
                e.consume();
                return;
            }

            double hVal = scrollPane.getHvalue();
            double vVal = scrollPane.getVvalue();

            double scale, factor;           
            if (e.getDeltaY() > 0) {
                factor = 1.1;
            } else {
                factor = 0.9;
            }
            scale = actualScale * factor;

            scale = Math.min(scale, maxScale);
            scale = Math.max(scale, minScale);

            canvas.setScaleX(scale);
            canvas.setScaleY(scale);

            scrollPane.setHvalue(hVal);
            scrollPane.setVvalue(vVal);

            e.consume();
        }
    }

}

Main.java

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class Main extends Application  {     

    public static final int CANVAS_WIDTH = 1000;
    public static final int CANVAS_HEIGHT = 1000;

    private final Pane canvas = new Pane();
    private final ScrollPane scrollPane = new ScrollPane(); 
    private final ZoomHandler zoomHandler = new ZoomHandler(scrollPane, canvas);

    public static void main(String[] args) { launch(args); }
    @Override public void start(Stage primaryStage) {        
        canvas.setPrefWidth(CANVAS_WIDTH);
        canvas.setPrefHeight(CANVAS_HEIGHT);
        canvas.setOnScroll(zoomHandler);        

        scrollPane.setStyle("-fx-background-color: transparent; -fx-control-inner-background: transparent;");
        scrollPane.setContent(new Group(canvas));
        scrollPane.setPannable(true);
        scrollPane.setOnScroll(zoomHandler);

        addContent();

        Scene scene = new Scene(scrollPane, 800, 600);

        primaryStage.setTitle("Zoom");
        primaryStage.setScene(scene);
        primaryStage.show();

        scrollPane.setHvalue(0.5);  
        scrollPane.setVvalue(0.5);  
    }

    private void addContent() {
        Text hello = new Text(400, 500, "Hello");
        hello.setStyle("-fx-font-size: 20px; -fx-fill: red;");

        Text world = new Text(500, 600, "world!");
        world.setStyle("-fx-fill: blue;");

        Text text = new Text(200, 350, "How to zoom to this text when mouse is over?");
        text.setStyle("-fx-font-weight: bold;");

        Rectangle rect = new Rectangle(600, 450, 300, 200);
        rect.setFill(Color.GREEN);

        Circle circle = new Circle(250, 700, 100);
        circle.setFill(Color.YELLOW);

        canvas.getChildren().addAll(hello, world, text, rect, circle);
    }

}

有什么想法吗?提前致谢。

【问题讨论】:

    标签: javafx javafx-2 zooming scrollpane


    【解决方案1】:

    我遇到了同样的问题,我做了以下事情 - 我不认为这是一个正确的解决方案(而是一种解决方法),但它几乎可以正常工作。

    基本上,我会进行一些计算来检索 Hvalue 和 Vvalue 的正确值。此外,我必须应用一个补偿因子,因为 ScrollPane 不仅会进行图像的缩放,还会进行图像的垂直平移(由于 ScrollPane 的标准行为)。

    在这里你可以找到代码。

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

    【讨论】:

    • 抱歉复活了一个老问题,但是你能解释一下计算鼠标在图像中的相对位置背后的原因吗?那么mouseYProperty.get() 是获取鼠标在vewport 中的位置吗?然后你通过与 Vvalue 位置成比例的屏幕外空间量来抵消它?为什么要分呢?
    猜你喜欢
    • 2017-02-11
    • 2012-10-20
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    • 2022-01-06
    • 2021-10-15
    • 2020-05-28
    相关资源
    最近更新 更多