【问题标题】:Pick and move a node in a pannable/zoomable Pane在可平移/可缩放窗格中选择并移动节点
【发布时间】:2015-11-20 02:30:22
【问题描述】:

我试图在ScrollPane 中放置一些组件。这些组件应该能够通过鼠标(单击并拖动)在此窗格中移动。 ScrollPane 本身是可平移和可缩放的。

现在,如果我选择其中一个并将其拖动到新位置,如果我缩小了鼠标,则鼠标比组件快。放大时,组件的移动速度比鼠标移动快。

如果不缩放,它会一直工作,直到我到达 ScrollPane 自动平移的某个位置。

它必须对节点的确定坐标做一些事情。有谁知道我必须添加什么才能使其正常工作?

我的控制器类:

public class MainWindowController implements Initializable {

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

    @FXML
    private ScrollPane scrollPane;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        Node node1 = new Node("Test");
        Node node2 = new Node("Test2", 100, 200);

        group.getChildren().addAll(node1, node2);
        zoomPane.getChildren().add(group);
        Group scrollContent = new Group(zoomPane);
        scrollPane.setContent(scrollContent);
        scrollPane.viewportBoundsProperty().addListener((ObservableValue<? extends Bounds> observable,
                Bounds oldValue, Bounds newValue) -> {
                    zoomPane.setMinSize(newValue.getWidth(), newValue.getHeight());
                });

        zoomPane.setOnScroll(
                (ScrollEvent event) -> {
                    event.consume();
                    if (event.getDeltaY() == 0) {
                        return;
                    }
                    double scaleFactor = (event.getDeltaY() > 0) ? SCALE_DELTA : 1 / SCALE_DELTA;
                    Point2D scrollOffset = figureScrollOffset(scrollContent, scrollPane);
                    group.setScaleX(group.getScaleX() * scaleFactor);
                    group.setScaleY(group.getScaleY() * scaleFactor);
                    repositionScroller(scrollContent, scrollPane, scaleFactor, scrollOffset);
                }
        );

        group.getChildren()
                .add(new Node("Test3", 500, 500));

    }

    private Point2D figureScrollOffset(javafx.scene.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(javafx.scene.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());
        }
    }
}

节点类:

public class Node extends Parent {

    private NodeStatus status = NodeStatus.OK;
    private final Image okImage = new Image(getClass().getResourceAsStream("/images/MasterOK.png"));
    private ImageView image = new ImageView(okImage);
    private final Text label = new Text();
    private final Font font = Font.font("Courier", 20);
    double orgSceneX, orgSceneY;
    double layoutX, layoutY;

    public Node(String labelText) {
        this(labelText, 0, 0);
    }

    public Node(String labelText, double x, double y) {
        label.setText(labelText);
        label.setFont(font);
        label.setLayoutX(okImage.getWidth() + 10);
        label.setLayoutY(okImage.getHeight() / 2 + 10);
        getChildren().add(image);
        getChildren().add(label);
        setLayoutX(x);
        setLayoutY(y);
        setCursor(Cursor.MOVE);
        setOnMousePressed(new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent t) {
                orgSceneX = t.getSceneX();
                orgSceneY = t.getSceneY();
                layoutX = getLayoutX();
                layoutY = getLayoutY();
            }
        });
        setOnMouseDragged(new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent t) {
                setLayoutX(layoutX + t.getSceneX() - orgSceneX);
                setLayoutY(layoutY + t.getSceneY() - orgSceneY);
            }
        });

    }

    public NodeStatus getStatus() {
        return status;
    }

    public void setStatus(NodeStatus status) {
        this.status = status;
    }

}

class Delta {

    double x, y;
}

和 fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import nodes.*?>

<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="cqsmonitor.MainWindowController">
    <children>
        <Pane layoutX="666.0" layoutY="14.0" prefHeight="572.0" prefWidth="114.0" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0">
            <children>
                <TextField layoutY="30.0" prefHeight="25.0" prefWidth="114.0" />
                <Label layoutY="12.0" text="Search:" />
                <ChoiceBox layoutY="90.0" prefHeight="25.0" prefWidth="114.0" />
                <Label layoutY="73.0" text="View:" />
            </children>
        </Pane>
        <ScrollPane fx:id="scrollPane" layoutX="14.0" layoutY="14.0" pannable="true" prefHeight="571.0" prefWidth="644.0" AnchorPane.bottomAnchor="15.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="142.0" AnchorPane.topAnchor="14.0">
        </ScrollPane>
    </children>
</AnchorPane>

【问题讨论】:

    标签: java javafx javafx-8


    【解决方案1】:

    我只是发布一个答案来总结我的问题的解决方案:

    我的节点落后的问题是因为不知道上一级窗格中可能发生的缩放。因此,作为最终解决方案,我将我的代码与 Roland 的答案混合在一起。如果有人有进一步优化的想法,请发布。

    控制器类:

    public class MainWindowController implements Initializable {
    
        private final Group group = new Group();
        private static final double MAX_SCALE = 10.0d;
        private static final double MIN_SCALE = .1d;
    
        @FXML
        private ScrollPane scrollPane;
    
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            ZoomableCanvas canvas = new ZoomableCanvas();
            MasterNode node1 = new MasterNode("Test");
            MasterNode node2 = new MasterNode("Test", 100, 200);
            canvas.getChildren().add(node1);
            canvas.getChildren().add(node2);
            group.getChildren().add(canvas);
            scrollPane.setContent(group);
            scrollPane.addEventHandler(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
    
                @Override
                public void handle(ScrollEvent event) {
                    double delta = 1.2;
    
                    double scale = canvas.getScale(); // currently we only use Y, same value is used for X
                    double oldScale = scale;
    
                    if (event.getDeltaY() < 0) {
                        scale /= delta;
                    } else {
                        scale *= delta;
                    }
    
                    scale = clamp(scale, MIN_SCALE, MAX_SCALE);
    
                    double f = (scale / oldScale) - 1;
    
                    double dx = (event.getSceneX() - (canvas.getBoundsInParent().getWidth() / 2 + canvas.getBoundsInParent().getMinX()));
                    double dy = (event.getSceneY() - (canvas.getBoundsInParent().getHeight() / 2 + canvas.getBoundsInParent().getMinY()));
    
                    canvas.setScale(scale);
    
                    // note: pivot value must be untransformed, i. e. without scaling
                    canvas.setPivot(f * dx, f * dy);
    
                    event.consume();
                }
            });
    
        }
    
        private double clamp(double value, double min, double max) {
    
            if (Double.compare(value, min) < 0) {
                return min;
            }
    
            if (Double.compare(value, max) > 0) {
                return max;
            }
    
            return value;
        }
    }
    

    画布:

    public class ZoomableCanvas extends Pane {
    
        DoubleProperty scale = new SimpleDoubleProperty(1.0);
    
        public ZoomableCanvas() {
            scaleXProperty().bind(scale);
            scaleYProperty().bind(scale);
    
    
            getChildren().addListener((Change<? extends javafx.scene.Node> c) -> {
                while (c.next()) {
                    if (c.wasAdded()) {
                        for (Node child : c.getAddedSubList()) {
                            ((MasterNode) child).scaleProperty.bind(scale);
                        }
                    }
                    if (c.wasRemoved()) {
                        for (Node child : c.getRemoved()) {
                            ((MasterNode) child).scaleProperty.unbind();
                        }
                    }
                }
            });
        }
    
        public double getScale() {
            return scale.get();
        }
    
        public void setScale(double scale) {
            this.scale.set(scale);
        }
    
        public void setPivot(double x, double y) {
            setTranslateX(getTranslateX() - x);
            setTranslateY(getTranslateY() - y);
        }
    }
    

    节点:

    public class MasterNode extends Parent {
    
        private NodeStatus status = NodeStatus.OK;
        private final Image okImage = new Image(getClass().getResourceAsStream("/images/MasterOK.png"));
        private ImageView image = new ImageView(okImage);
        private final Text label = new Text();
        private final Font font = Font.font("Courier", 20);
        private DragContext nodeDragContext = new DragContext();
        public DoubleProperty scaleProperty = new SimpleDoubleProperty(1.0);
        double orgSceneX, orgSceneY;
        double layoutX, layoutY;
    
        public MasterNode(String labelText) {
            this(labelText, 0, 0);
        }
    
        public MasterNode(String labelText, double x, double y) {
            scaleXProperty().bind(scaleProperty);
            scaleYProperty().bind(scaleProperty);
            label.setText(labelText);
            label.setFont(font);
            label.setLayoutX(okImage.getWidth() + 10);
            label.setLayoutY(okImage.getHeight() / 2 + 10);
            getChildren().add(image);
            getChildren().add(label);
            setLayoutX(x);
            setLayoutY(y);
            setCursor(Cursor.MOVE);
            setOnMousePressed(new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
                    // left mouse button => dragging
                    if (!event.isPrimaryButtonDown()) {
                        return;
                    }
    
                    nodeDragContext.setMouseAnchorX(event.getSceneX());
                    nodeDragContext.setMouseAnchorY(event.getSceneY());
    
                    Node node = (Node) event.getSource();
    
                    nodeDragContext.setTranslateAnchorX(node.getTranslateX());
                    nodeDragContext.setTranslateAnchorY(node.getTranslateY());
    
                }
            });
            setOnMouseDragged(new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
                    // left mouse button => dragging
                    if (!event.isPrimaryButtonDown()) {
                        return;
                    }
                    Node node = (Node) event.getSource();
                    node.setTranslateX(nodeDragContext.getTranslateAnchorX() + ((event.getSceneX() - nodeDragContext.getMouseAnchorX()) / getScale()));
                    node.setTranslateY(nodeDragContext.getTranslateAnchorY() + ((event.getSceneY() - nodeDragContext.getMouseAnchorY()) / getScale()));
                    event.consume();
                }
            });
    
        }
    
        public double getScale() {
            return scaleProperty.get();
        }
    
        public void setScale(double scale) {
            scaleProperty.set(scale);
        }
    
        public NodeStatus getStatus() {
            return status;
        }
    
        public void setStatus(NodeStatus status) {
            this.status = status;
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      由于还没有人回答,这里有一些代码。我不想挖掘你的并重新发明轮子。

      您可以通过鼠标左键拖动来移动节点,使用鼠标滚轮缩放窗格,使用鼠标右键平移窗格。不需要 ScrollPane。但是,如果您想要滚动条,您可以随时添加它们。

      代码:

      import javafx.application.Application;
      import javafx.beans.property.DoubleProperty;
      import javafx.beans.property.SimpleDoubleProperty;
      import javafx.event.EventHandler;
      import javafx.scene.Group;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.canvas.Canvas;
      import javafx.scene.canvas.GraphicsContext;
      import javafx.scene.control.Label;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.input.ScrollEvent;
      import javafx.scene.layout.Pane;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.Circle;
      import javafx.scene.shape.Rectangle;
      import javafx.stage.Stage;
      
      /**
       * The canvas which holds all of the nodes of the application.
       */
      class PannableCanvas extends Pane {
      
          DoubleProperty myScale = new SimpleDoubleProperty(1.0);
      
          public PannableCanvas() {
      
              setPrefSize(600, 600);
              setStyle("-fx-background-color: lightgrey; -fx-border-color: blue;");
      
              // add scale transform
              scaleXProperty().bind(myScale);
              scaleYProperty().bind(myScale);
      
              // logging
              addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { 
                  System.out.println( 
                          "canvas event: " + ( ((event.getSceneX() - getBoundsInParent().getMinX()) / getScale()) + ", scale: " + getScale())
                          );
                  System.out.println( "canvas bounds: " + getBoundsInParent());   
                      });
      
          }
      
          /**
           * Add a grid to the canvas, send it to back
           */
          public void addGrid() {
      
              double w = getBoundsInLocal().getWidth();
              double h = getBoundsInLocal().getHeight();
      
              // add grid
              Canvas grid = new Canvas(w, h);
      
              // don't catch mouse events
              grid.setMouseTransparent(true);
      
              GraphicsContext gc = grid.getGraphicsContext2D();
      
              gc.setStroke(Color.GRAY);
              gc.setLineWidth(1);
      
              // draw grid lines
              double offset = 50;
              for( double i=offset; i < w; i+=offset) {
                  // vertical
                  gc.strokeLine( i, 0, i, h);
                  // horizontal
                  gc.strokeLine( 0, i, w, i);
              }
      
              getChildren().add( grid);
      
              grid.toBack();
          }
      
          public double getScale() {
              return myScale.get();
          }
      
          /**
           * Set x/y scale
           * @param myScale
           */
          public void setScale( double scale) {
              myScale.set(scale);
          }
      
          /**
           * Set x/y pivot points
           * @param x
           * @param y
           */
          public void setPivot( double x, double y) {
              setTranslateX(getTranslateX()-x);
              setTranslateY(getTranslateY()-y);
          }
      }
      
      
      /**
       * Mouse drag context used for scene and nodes.
       */
      class DragContext {
      
          double mouseAnchorX;
          double mouseAnchorY;
      
          double translateAnchorX;
          double translateAnchorY;
      
      }
      
      /**
       * Listeners for making the nodes draggable via left mouse button. Considers if parent is zoomed.
       */
      class NodeGestures {
      
          private DragContext nodeDragContext = new DragContext();
      
          PannableCanvas canvas;
      
          public NodeGestures( PannableCanvas canvas) {
              this.canvas = canvas;
      
          }
      
          public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
              return onMousePressedEventHandler;
          }
      
          public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
              return onMouseDraggedEventHandler;
          }
      
          private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
      
              public void handle(MouseEvent event) {
      
                  // left mouse button => dragging
                  if( !event.isPrimaryButtonDown())
                      return;
      
                  nodeDragContext.mouseAnchorX = event.getSceneX();
                  nodeDragContext.mouseAnchorY = event.getSceneY();
      
                  Node node = (Node) event.getSource();
      
                  nodeDragContext.translateAnchorX = node.getTranslateX();
                  nodeDragContext.translateAnchorY = node.getTranslateY();
      
              }
      
          };
      
          private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
              public void handle(MouseEvent event) {
      
                  // left mouse button => dragging
                  if( !event.isPrimaryButtonDown())
                      return;
      
                  double scale = canvas.getScale();
      
                  Node node = (Node) event.getSource();
      
                  node.setTranslateX(nodeDragContext.translateAnchorX + (( event.getSceneX() - nodeDragContext.mouseAnchorX) / scale));
                  node.setTranslateY(nodeDragContext.translateAnchorY + (( event.getSceneY() - nodeDragContext.mouseAnchorY) / scale));
      
                  event.consume();
      
              }
          };
      }
      
      /**
       * Listeners for making the scene's canvas draggable and zoomable
       */
      class SceneGestures {
      
          private static final double MAX_SCALE = 10.0d;
          private static final double MIN_SCALE = .1d;
      
          private DragContext sceneDragContext = new DragContext();
      
          PannableCanvas canvas;
      
          public SceneGestures( PannableCanvas canvas) {
              this.canvas = canvas;
          }
      
          public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
              return onMousePressedEventHandler;
          }
      
          public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
              return onMouseDraggedEventHandler;
          }
      
          public EventHandler<ScrollEvent> getOnScrollEventHandler() {
              return onScrollEventHandler;
          }
      
          private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
      
              public void handle(MouseEvent event) {
      
                  // right mouse button => panning
                  if( !event.isSecondaryButtonDown())
                      return;
      
                  sceneDragContext.mouseAnchorX = event.getSceneX();
                  sceneDragContext.mouseAnchorY = event.getSceneY();
      
                  sceneDragContext.translateAnchorX = canvas.getTranslateX();
                  sceneDragContext.translateAnchorY = canvas.getTranslateY();
      
              }
      
          };
      
          private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
              public void handle(MouseEvent event) {
      
                  // right mouse button => panning
                  if( !event.isSecondaryButtonDown())
                      return;
      
                  canvas.setTranslateX(sceneDragContext.translateAnchorX + event.getSceneX() - sceneDragContext.mouseAnchorX);
                  canvas.setTranslateY(sceneDragContext.translateAnchorY + event.getSceneY() - sceneDragContext.mouseAnchorY);
      
                  event.consume();
              }
          };
      
          /**
           * Mouse wheel handler: zoom to pivot point
           */
          private EventHandler<ScrollEvent> onScrollEventHandler = new EventHandler<ScrollEvent>() {
      
              @Override
              public void handle(ScrollEvent event) {
      
                  double delta = 1.2;
      
                  double scale = canvas.getScale(); // currently we only use Y, same value is used for X
                  double oldScale = scale;
      
                  if (event.getDeltaY() < 0)
                      scale /= delta;
                  else
                      scale *= delta;
      
                  scale = clamp( scale, MIN_SCALE, MAX_SCALE);
      
                  double f = (scale / oldScale)-1;
      
                  double dx = (event.getSceneX() - (canvas.getBoundsInParent().getWidth()/2 + canvas.getBoundsInParent().getMinX()));
                  double dy = (event.getSceneY() - (canvas.getBoundsInParent().getHeight()/2 + canvas.getBoundsInParent().getMinY()));
      
                  canvas.setScale( scale);
      
                  // note: pivot value must be untransformed, i. e. without scaling
                  canvas.setPivot(f*dx, f*dy);
      
                  event.consume();
      
              }
      
          };
      
      
          public static double clamp( double value, double min, double max) {
      
              if( Double.compare(value, min) < 0)
                  return min;
      
              if( Double.compare(value, max) > 0)
                  return max;
      
              return value;
          }
      }
      
      
      
      /**
       * An application with a zoomable and pannable canvas.
       */
      public class ZoomAndScrollApplication extends Application {
          public static void main(String[] args) {
              launch(args);
          }
      
          @Override
          public void start(Stage stage) {
      
              Group group = new Group();
      
              // create canvas
              PannableCanvas canvas = new PannableCanvas();
      
              // we don't want the canvas on the top/left in this example => just
              // translate it a bit
              canvas.setTranslateX(100);
              canvas.setTranslateY(100);
      
              // create sample nodes which can be dragged
              NodeGestures nodeGestures = new NodeGestures( canvas);
      
              Label label1 = new Label("Draggable node 1");
              label1.setTranslateX(10);
              label1.setTranslateY(10);
              label1.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
              label1.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
      
              Label label2 = new Label("Draggable node 2");
              label2.setTranslateX(100);
              label2.setTranslateY(100);
              label2.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
              label2.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
      
              Label label3 = new Label("Draggable node 3");
              label3.setTranslateX(200);
              label3.setTranslateY(200);
              label3.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
              label3.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
      
              Circle circle1 = new Circle( 300, 300, 50);
              circle1.setStroke(Color.ORANGE);
              circle1.setFill(Color.ORANGE.deriveColor(1, 1, 1, 0.5));
              circle1.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
              circle1.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
      
              Rectangle rect1 = new Rectangle(100,100);
              rect1.setTranslateX(450);
              rect1.setTranslateY(450);
              rect1.setStroke(Color.BLUE);
              rect1.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.5));
              rect1.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
              rect1.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
      
              canvas.getChildren().addAll(label1, label2, label3, circle1, rect1);
      
              group.getChildren().add(canvas);
      
              // create scene which can be dragged and zoomed
              Scene scene = new Scene(group, 1024, 768);
      
              SceneGestures sceneGestures = new SceneGestures(canvas);
              scene.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
              scene.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
              scene.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
      
              stage.setScene(scene);
              stage.show();
      
              canvas.addGrid();
      
          }
      }
      

      如果这对您没有任何帮助,请告诉我,然后我将删除该帖子。我对赏金不感兴趣。

      【讨论】:

      • 谢谢。我去看看。
      • 这是开箱即用的。我会尝试在我的代码中实现它,并在我有时间实现它时发布更新。
      • 这很有帮助,谢谢!我已经使用了您的示例并在其上添加了我的节点(一个组)。变焦也很完美!不过我有一个问题:是否可以缩放窗格,而不缩放其子级并保持其在窗格内的位置?想想谷歌地图标记。放大时,标记不会变大。我怎样才能做到这一点?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-13
      • 2015-11-10
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 2012-05-12
      • 2013-12-17
      相关资源
      最近更新 更多