gpt4 book ai didi

JavaFX 正确缩放

转载 作者:行者123 更新时间:2023-12-03 15:14:23 33 4
gpt4 key购买 nike

我想在滚动事件上缩放 Pane 中的所有节点。

到目前为止我已经尝试过:

  • 当我做 scaleX 或 scaleY 时, Pane 的边框
    分别缩放(在设置 Pane 样式 -fx-border-color: black; 时看到)。所以如果我不是来自边境,不是每个事件都会开始
    Pane ,所以我需要它。 enter image description here
  • 下一步我尝试扩展每个节点,结果非常糟糕,
    像这样的东西 - (通过点延伸的线)。或者如果
    在另一边滚动,它会少enter image description here
  • 我尝试的另一种方法是缩放节点的点。更好,但是
    我不喜欢它。看起来像point.setScaleX(point.getScaleX()+scaleX)对于 y 和其他节点
    适本地。
  • 最佳答案

    我创建了一个示例应用程序来演示一种在滚动事件的视口(viewport)中执行节点缩放的方法(例如,通过滚动鼠标滚轮滚动进出)。

    用于缩放放置在 StackPane 中的组的示例的关键逻辑:

    final double SCALE_DELTA = 1.1;
    final StackPane zoomPane = new StackPane();

    zoomPane.getChildren().add(group);
    zoomPane.setOnScroll(new EventHandler<ScrollEvent>() {
    @Override public void handle(ScrollEvent event) {
    event.consume();

    if (event.getDeltaY() == 0) {
    return;
    }

    double scaleFactor =
    (event.getDeltaY() > 0)
    ? SCALE_DELTA
    : 1/SCALE_DELTA;

    group.setScaleX(group.getScaleX() * scaleFactor);
    group.setScaleY(group.getScaleY() * scaleFactor);
    }
    });

    滚动事件处理程序设置在封闭的 StackPane 上,这是一个可调整大小的 Pane ,因此它会扩展以填充任何空白空间,从而使缩放的内容保持在 Pane 的中心。如果您在 StackPane 内的任意位置移动鼠标滚轮,它将放大或缩小封闭的节点组。

    zoomy zoomyin
    import javafx.application.Application;
    import javafx.beans.value.*;
    import javafx.event.*;
    import javafx.geometry.Bounds;
    import javafx.scene.*;
    import javafx.scene.control.*;
    import javafx.scene.image.*;
    import javafx.scene.input.*;
    import javafx.scene.layout.*;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.*;
    import javafx.stage.Stage;

    public class GraphicsScalingApp extends Application {
    public static void main(String[] args) { launch(args); }

    @Override public void start(final Stage stage) {
    final Group group = new Group(
    createStar(),
    createCurve()
    );

    Parent zoomPane = createZoomPane(group);

    VBox layout = new VBox();
    layout.getChildren().setAll(
    createMenuBar(stage, group),
    zoomPane
    );

    VBox.setVgrow(zoomPane, Priority.ALWAYS);
    Scene scene = new Scene(
    layout
    );

    stage.setTitle("Zoomy");
    stage.getIcons().setAll(new Image(APP_ICON));
    stage.setScene(scene);
    stage.show();
    }

    private Parent createZoomPane(final Group group) {
    final double SCALE_DELTA = 1.1;
    final StackPane zoomPane = new StackPane();

    zoomPane.getChildren().add(group);
    zoomPane.setOnScroll(new EventHandler<ScrollEvent>() {
    @Override public void handle(ScrollEvent event) {
    event.consume();

    if (event.getDeltaY() == 0) {
    return;
    }

    double scaleFactor =
    (event.getDeltaY() > 0)
    ? SCALE_DELTA
    : 1/SCALE_DELTA;

    group.setScaleX(group.getScaleX() * scaleFactor);
    group.setScaleY(group.getScaleY() * scaleFactor);
    }
    });

    zoomPane.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
    @Override public void changed(ObservableValue<? extends Bounds> observable, Bounds oldBounds, Bounds bounds) {
    zoomPane.setClip(new Rectangle(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight()));
    }
    });

    return zoomPane;
    }

    private SVGPath createCurve() {
    SVGPath ellipticalArc = new SVGPath();
    ellipticalArc.setContent(
    "M10,150 A15 15 180 0 1 70 140 A15 25 180 0 0 130 130 A15 55 180 0 1 190 120"
    );
    ellipticalArc.setStroke(Color.LIGHTGREEN);
    ellipticalArc.setStrokeWidth(4);
    ellipticalArc.setFill(null);
    return ellipticalArc;
    }

    private SVGPath createStar() {
    SVGPath star = new SVGPath();
    star.setContent(
    "M100,10 L100,10 40,180 190,60 10,60 160,180 z"
    );
    star.setStrokeLineJoin(StrokeLineJoin.ROUND);
    star.setStroke(Color.BLUE);
    star.setFill(Color.DARKBLUE);
    star.setStrokeWidth(4);
    return star;
    }

    private MenuBar createMenuBar(final Stage stage, final Group group) {
    Menu fileMenu = new Menu("_File");
    MenuItem exitMenuItem = new MenuItem("E_xit");
    exitMenuItem.setGraphic(new ImageView(new Image(CLOSE_ICON)));
    exitMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent event) {
    stage.close();
    }
    });
    fileMenu.getItems().setAll(
    exitMenuItem
    );
    Menu zoomMenu = new Menu("_Zoom");
    MenuItem zoomResetMenuItem = new MenuItem("Zoom _Reset");
    zoomResetMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.ESCAPE));
    zoomResetMenuItem.setGraphic(new ImageView(new Image(ZOOM_RESET_ICON)));
    zoomResetMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent event) {
    group.setScaleX(1);
    group.setScaleY(1);
    }
    });
    MenuItem zoomInMenuItem = new MenuItem("Zoom _In");
    zoomInMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.I));
    zoomInMenuItem.setGraphic(new ImageView(new Image(ZOOM_IN_ICON)));
    zoomInMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent event) {
    group.setScaleX(group.getScaleX() * 1.5);
    group.setScaleY(group.getScaleY() * 1.5);
    }
    });
    MenuItem zoomOutMenuItem = new MenuItem("Zoom _Out");
    zoomOutMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.O));
    zoomOutMenuItem.setGraphic(new ImageView(new Image(ZOOM_OUT_ICON)));
    zoomOutMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent event) {
    group.setScaleX(group.getScaleX() * 1/1.5);
    group.setScaleY(group.getScaleY() * 1/1.5);
    }
    });
    zoomMenu.getItems().setAll(
    zoomResetMenuItem,
    zoomInMenuItem,
    zoomOutMenuItem
    );
    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().setAll(
    fileMenu,
    zoomMenu
    );
    return menuBar;
    }

    // icons source from: http://www.iconarchive.com/show/soft-scraps-icons-by-deleket.html
    // icon license: CC Attribution-Noncommercial-No Derivate 3.0 =? http://creativecommons.org/licenses/by-nc-nd/3.0/
    // icon Commercial usage: Allowed (Author Approval required -> Visit artist website for details).

    public static final String APP_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/128/Zoom-icon.png";
    public static final String ZOOM_RESET_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Zoom-icon.png";
    public static final String ZOOM_OUT_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Zoom-Out-icon.png";
    public static final String ZOOM_IN_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Zoom-In-icon.png";
    public static final String CLOSE_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Button-Close-icon.png";
    }

    更新 ScrollPane 中的缩放节点

    就目前而言,上述实现运行良好,但能够将缩放节点放置在滚动 Pane 内很有用,这样当您放大使缩放节点大于可用视口(viewport)时,您仍然可以平移滚动 Pane 中的缩放节点以查看节点的某些部分。

    我发现实现放大滚动 Pane 的行为很困难,所以我在 Oracle JavaFX Forum thread 上寻求帮助.

    Oracle JavaFX 论坛用户 James_D 提出了以下解决方案,它很好地解决了 ScrollPane 中的缩放问题。

    他的评论和代码如下:

    A couple of minor changes first: I wrapped the StackPane in a Group so that the ScrollPane would be aware of the changes to the transforms, as per the ScrollPane Javadocs. And then I bound the minimum size of the StackPane to the viewport size (keeping the content centered when smaller than the viewport).

    Initially I thought I should use a Scale transform to zoom around the displayed center (i.e. the point on the content that is at the center of the viewport). But I found I still needed to fix the scroll position afterwards to keep the same displayed center, so I abandoned that and reverted to using setScaleX() and setScaleY().

    The trick is to fix the scroll position after scaling. I computed the scroll offset in local coordinates of the scroll content, and then computed the new scroll values needed after the scale. This was a little tricky. The basic observation is that (hValue-hMin)/(hMax-hMin) = x / (contentWidth - viewportWidth), where x is the horizontal offset of the left edge of the viewport from the left edge of the content. Then you have centerX = x + viewportWidth/2.

    After scaling, the x coordinate of the old centerX is now centerX*scaleFactor. So we just have to set the new hValue to make that the new center. There's a bit of algebra to figure that out.

    After that, panning by dragging was pretty easy :).



    添加高级 API 以支持 ScrollPane 中的缩放和缩放功能的相应功能请求是 Add scaleContent functionality to ScrollPane .如果您希望看到它的实现,请投票或评论功能请求。
    import javafx.application.Application;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.beans.value.*;
    import javafx.event.*;
    import javafx.geometry.Bounds;
    import javafx.geometry.Point2D;
    import javafx.scene.*;
    import javafx.scene.control.*;
    import javafx.scene.image.*;
    import javafx.scene.input.*;
    import javafx.scene.layout.*;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.*;
    import javafx.stage.Stage;

    public class GraphicsScalingApp extends Application {
    public static void main(String[] args) {
    launch(args);
    }

    @Override
    public void start(final Stage stage) {
    final Group group = new Group(createStar(), createCurve());

    Parent zoomPane = createZoomPane(group);

    VBox layout = new VBox();
    layout.getChildren().setAll(createMenuBar(stage, group), zoomPane);

    VBox.setVgrow(zoomPane, Priority.ALWAYS);

    Scene scene = new Scene(layout);

    stage.setTitle("Zoomy");
    stage.getIcons().setAll(new Image(APP_ICON));
    stage.setScene(scene);
    stage.show();
    }

    private Parent createZoomPane(final Group group) {
    final double SCALE_DELTA = 1.1;
    final StackPane zoomPane = new StackPane();

    zoomPane.getChildren().add(group);

    final ScrollPane scroller = new ScrollPane();
    final Group scrollContent = new Group(zoomPane);
    scroller.setContent(scrollContent);

    scroller.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() {
    @Override
    public void changed(ObservableValue<? extends Bounds> observable,
    Bounds oldValue, Bounds newValue) {
    zoomPane.setMinSize(newValue.getWidth(), newValue.getHeight());
    }
    });

    scroller.setPrefViewportWidth(256);
    scroller.setPrefViewportHeight(256);

    zoomPane.setOnScroll(new EventHandler<ScrollEvent>() {
    @Override
    public void handle(ScrollEvent event) {
    event.consume();

    if (event.getDeltaY() == 0) {
    return;
    }

    double scaleFactor = (event.getDeltaY() > 0) ? SCALE_DELTA
    : 1 / SCALE_DELTA;

    // amount of scrolling in each direction in scrollContent coordinate
    // units
    Point2D scrollOffset = figureScrollOffset(scrollContent, scroller);

    group.setScaleX(group.getScaleX() * scaleFactor);
    group.setScaleY(group.getScaleY() * scaleFactor);

    // move viewport so that old center remains in the center after the
    // scaling
    repositionScroller(scrollContent, scroller, scaleFactor, scrollOffset);

    }
    });

    // Panning via drag....
    final ObjectProperty<Point2D> lastMouseCoordinates = new SimpleObjectProperty<Point2D>();
    scrollContent.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
    lastMouseCoordinates.set(new Point2D(event.getX(), event.getY()));
    }
    });

    scrollContent.setOnMouseDragged(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
    double deltaX = event.getX() - lastMouseCoordinates.get().getX();
    double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth();
    double deltaH = deltaX * (scroller.getHmax() - scroller.getHmin()) / extraWidth;
    double desiredH = scroller.getHvalue() - deltaH;
    scroller.setHvalue(Math.max(0, Math.min(scroller.getHmax(), desiredH)));

    double deltaY = event.getY() - lastMouseCoordinates.get().getY();
    double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight();
    double deltaV = deltaY * (scroller.getHmax() - scroller.getHmin()) / extraHeight;
    double desiredV = scroller.getVvalue() - deltaV;
    scroller.setVvalue(Math.max(0, Math.min(scroller.getVmax(), desiredV)));
    }
    });

    return scroller;
    }

    private Point2D figureScrollOffset(Node scrollContent, ScrollPane scroller) {
    double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth();
    double hScrollProportion = (scroller.getHvalue() - scroller.getHmin()) / (scroller.getHmax() - scroller.getHmin());
    double scrollXOffset = hScrollProportion * Math.max(0, extraWidth);
    double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight();
    double vScrollProportion = (scroller.getVvalue() - scroller.getVmin()) / (scroller.getVmax() - scroller.getVmin());
    double scrollYOffset = vScrollProportion * Math.max(0, extraHeight);
    return new Point2D(scrollXOffset, scrollYOffset);
    }

    private void repositionScroller(Node scrollContent, ScrollPane scroller, double scaleFactor, Point2D scrollOffset) {
    double scrollXOffset = scrollOffset.getX();
    double scrollYOffset = scrollOffset.getY();
    double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth();
    if (extraWidth > 0) {
    double halfWidth = scroller.getViewportBounds().getWidth() / 2 ;
    double newScrollXOffset = (scaleFactor - 1) * halfWidth + scaleFactor * scrollXOffset;
    scroller.setHvalue(scroller.getHmin() + newScrollXOffset * (scroller.getHmax() - scroller.getHmin()) / extraWidth);
    } else {
    scroller.setHvalue(scroller.getHmin());
    }
    double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight();
    if (extraHeight > 0) {
    double halfHeight = scroller.getViewportBounds().getHeight() / 2 ;
    double newScrollYOffset = (scaleFactor - 1) * halfHeight + scaleFactor * scrollYOffset;
    scroller.setVvalue(scroller.getVmin() + newScrollYOffset * (scroller.getVmax() - scroller.getVmin()) / extraHeight);
    } else {
    scroller.setHvalue(scroller.getHmin());
    }
    }

    private SVGPath createCurve() {
    SVGPath ellipticalArc = new SVGPath();
    ellipticalArc.setContent("M10,150 A15 15 180 0 1 70 140 A15 25 180 0 0 130 130 A15 55 180 0 1 190 120");
    ellipticalArc.setStroke(Color.LIGHTGREEN);
    ellipticalArc.setStrokeWidth(4);
    ellipticalArc.setFill(null);
    return ellipticalArc;
    }

    private SVGPath createStar() {
    SVGPath star = new SVGPath();
    star.setContent("M100,10 L100,10 40,180 190,60 10,60 160,180 z");
    star.setStrokeLineJoin(StrokeLineJoin.ROUND);
    star.setStroke(Color.BLUE);
    star.setFill(Color.DARKBLUE);
    star.setStrokeWidth(4);
    return star;
    }

    private MenuBar createMenuBar(final Stage stage, final Group group) {
    Menu fileMenu = new Menu("_File");
    MenuItem exitMenuItem = new MenuItem("E_xit");
    exitMenuItem.setGraphic(new ImageView(new Image(CLOSE_ICON)));
    exitMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
    stage.close();
    }
    });
    fileMenu.getItems().setAll(exitMenuItem);
    Menu zoomMenu = new Menu("_Zoom");
    MenuItem zoomResetMenuItem = new MenuItem("Zoom _Reset");
    zoomResetMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.ESCAPE));
    zoomResetMenuItem.setGraphic(new ImageView(new Image(ZOOM_RESET_ICON)));
    zoomResetMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
    group.setScaleX(1);
    group.setScaleY(1);
    }
    });
    MenuItem zoomInMenuItem = new MenuItem("Zoom _In");
    zoomInMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.I));
    zoomInMenuItem.setGraphic(new ImageView(new Image(ZOOM_IN_ICON)));
    zoomInMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
    group.setScaleX(group.getScaleX() * 1.5);
    group.setScaleY(group.getScaleY() * 1.5);
    }
    });
    MenuItem zoomOutMenuItem = new MenuItem("Zoom _Out");
    zoomOutMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.O));
    zoomOutMenuItem.setGraphic(new ImageView(new Image(ZOOM_OUT_ICON)));
    zoomOutMenuItem.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
    group.setScaleX(group.getScaleX() * 1 / 1.5);
    group.setScaleY(group.getScaleY() * 1 / 1.5);
    }
    });
    zoomMenu.getItems().setAll(zoomResetMenuItem, zoomInMenuItem,
    zoomOutMenuItem);
    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().setAll(fileMenu, zoomMenu);
    return menuBar;
    }

    // icons source from:
    // http://www.iconarchive.com/show/soft-scraps-icons-by-deleket.html
    // icon license: CC Attribution-Noncommercial-No Derivate 3.0 =?
    // http://creativecommons.org/licenses/by-nc-nd/3.0/
    // icon Commercial usage: Allowed (Author Approval required -> Visit artist
    // website for details).

    public static final String APP_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/128/Zoom-icon.png";
    public static final String ZOOM_RESET_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Zoom-icon.png";
    public static final String ZOOM_OUT_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Zoom-Out-icon.png";
    public static final String ZOOM_IN_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Zoom-In-icon.png";
    public static final String CLOSE_ICON = "http://icons.iconarchive.com/icons/deleket/soft-scraps/24/Button-Close-icon.png";
    }

    关于JavaFX 正确缩放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16680295/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com