当前位置: 首页 > 知识库问答 >
问题:

JavaFX--相对于鼠标位置缩放

时恩
2023-03-14
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();
        }
    }

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

}

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

共有1个答案

马银龙
2023-03-14

我也遇到了同样的问题,我做了以下工作--我不认为这是一个合适的解决方案(更确切地说是一个变通方法),但它基本上很好。

基本上,我做一些计算来检索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()));
    }
}
 类似资料:
  • 问题内容: 我需要在滚动窗格上相对于鼠标位置放大/缩小。 我目前通过将内容包装在一个组中并缩放组本身来实现缩放功能。我用自定义枢轴创建一个新的Scale对象。(枢轴设置为鼠标位置) 这在Group的初始比例为1.0的情况下非常适用,但是之后的缩放比例无法沿正确的方向缩放-我相信这是因为Group缩放后相对鼠标位置会发生变化。 我的代码: 如何在不同的缩放级别上获得正确的鼠标位置?有没有一种更简单的

  • 我需要相对于鼠标位置放大/缩小滚动窗格。 我目前通过将我的内容包装在一个组中并缩放组本身来实现缩放功能。我创建了一个带有自定义透视的新Scale对象。(枢轴设置为鼠标位置) 如何在不同级别的缩放中获得正确的鼠标位置?是否有一种完全不同的方法来缩放滚动窗格更容易?

  • 问题内容: 如何按照自然风格对齐工具提示:鼠标指针的右下角? 问题答案: 对于默认的工具提示行为,只需添加属性。但是它不能包含图像。 在您弄清楚我是用纯JavaScript完成此问题之前,希望您发现它有用。图像将弹出并跟随鼠标。 JavaScript CSS 扩展多个元素 多个元素的一种解决方案是更新所有工具提示,并在鼠标移动时将其设置在光标下方。

  • 问题内容: 我想使用画布制作一个绘画应用程序。所以我需要找到鼠标在画布上的位置。 问题答案: 对于使用JQuery的人: 有时,当您拥有嵌套元素时,其中一个元素会附加事件,这可能会使您难以理解浏览器将其视为父级。在这里,您可以指定哪个父级。 您采用鼠标位置,然后从父元素的偏移位置中减去它。 如果要在滚动窗格内的页面上获取鼠标位置: 或相对于页面的位置: 请注意以下性能优化: 这样,JQuery不必

  • 问题内容: 我有一个40x40的网格,显示地图。当前,我有以下方法监听右键单击: 我要实现以下内容: 如果右键单击后选择了菜单项,则我想将右键单击的位置保存在变量中, 例如[32,12](如果右键单击跨越了32个网格窗格,向上为12块)。 关于如何执行此操作的任何建议? 提前致谢 问题答案: 向网格中的每个单元格添加一个侦听器,而不是向舞台添加一个鼠标侦听器。用于上下文菜单处理的适当侦听器是处理程

  • 我正在尝试突出显示由鼠标悬停的瓷砖。这是我的代码: 编辑: 光标对象不在鼠标下面,我如何修复和对齐它?