2014-12-08 9 views
10

Ich versuche, eine Anwendung mit einem zoombaren/pannable Canvas zu erstellen.Skalierung am Drehpunkt in einem bereits skalierten Knoten

Eigenschaften:

  • Zoom in/out mit an Drehpunkten Mausrad
  • ziehen die Knoten auf der Leinwand mit der linken Maustaste um
  • ziehen die gesamte Leinwand mit der rechten Maustaste

Das Zoomen am Pivot-Punkt funktioniert so lange, wie Sie das Zoomen mit Skalierung 1 starten. Positionieren Sie die Maus über einen Rasterpunkt und scrollen Sie mit t er Mausrad. Der Drehpunkt bleibt dort, wo Sie mit dem Zoomen begonnen haben.

Problem:

Wenn Sie die Ansicht vergrößern, dann mit der Maus auf einen anderen Punkt bewegen und wieder vergrößern, dann wird der Drehpunkt verschoben und Zoomen geschieht nicht mehr in der Anfangsmausposition.

Beispiel:

Hier ist der Code:

import javafx.application.Application; 
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.scene.transform.Scale; 
import javafx.stage.Stage; 

/** 
* The canvas which holds all of the nodes of the application. 
*/ 
class PannableCanvas extends Pane { 

    Scale scaleTransform; 

    public PannableCanvas() { 

     setPrefSize(600, 600); 
     setStyle("-fx-background-color: lightgrey; -fx-border-color: blue;"); 

     // add scale transform 
     scaleTransform = new Scale(1.0, 1.0); 
     getTransforms().add(scaleTransform); 

     // 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 Scale getScaleTransform() { 
     return scaleTransform; 
    } 

    public double getScale() { 
     return scaleTransform.getY(); 
    } 

    /** 
    * Set x/y scale 
    * @param scale 
    */ 
    public void setScale(double scale) { 
     scaleTransform.setX(scale); 
     scaleTransform.setY(scale); 
    } 

    /** 
    * Set x/y pivot points 
    * @param x 
    * @param y 
    */ 
    public void setPivot(double x, double y) { 
     scaleTransform.setPivotX(x); 
     scaleTransform.setPivotY(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; 

      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; 

      if (scale <= MIN_SCALE) { 
       scale = MIN_SCALE; 
      } else if (scale >= MAX_SCALE) { 
       scale = MAX_SCALE; 
      } 

      // pivot value must be untransformed, i. e. without scaling 
      canvas.setPivot( 
        ((event.getSceneX() - canvas.getBoundsInParent().getMinX())/oldScale), 
        ((event.getSceneY() - canvas.getBoundsInParent().getMinY())/oldScale) 
        ); 

      canvas.setScale(scale); 

      System.out.println("new pivot x: " + canvas.scaleTransform.getPivotX() + "/" + canvas.scaleTransform.getPivotY() + ", new scale: " + scale); 
      System.out.println("bounds: " + canvas.getBoundsInParent());  

      event.consume(); 

     } 

    }; 


} 

/** 
* An application with a zoomable and pannable canvas. 
*/ 
public class ScrollApplication 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(); 

    } 
} 

Es ist offensichtlich etwas falsch mit dem Drehpunkt Berechnung, aber ich kann nicht herausfinden, was es ist und wie man es beheben.

Vielen Dank!

enter image description here

Antwort

13

Zuerst würde ich empfehlen, nicht in linearen Schritten zu skalieren, sondern von Faktoren, die die Skalierung zu glätten:

  double delta = 1.2; 
      if (event.getDeltaY() < 0) 
       scale /= delta; 
      else 
       scale *= delta; 

... und irgendwie herrisch zu sein, ich geschweiften Klammern als guten Stil zu empfehlen; -):

   double delta = 1.2; 
       if (event.getDeltaY() < 0) { 
        scale /= delta; 
       } else { 
        scale *= delta; 
       } 

... und die Maus Scroll-Wert für eine noch bessere Qualität zu verwenden:

   double delta = 1.2; 
       if (event.getDeltaY() < 0) { 
        scale /= Math.pow(delta, -event.getDeltaY()/20); 
       } else { 
        scale *= Math.pow(delta, event.getDeltaY()/20); 
       } 

...das ist schließlich das gleiche wie:

   scale *= Math.pow(1.01, event.getDeltaY()); 

Second Ich empfehle die Leinwand übersetzen und Skalierungseigenschaften anstelle einer Transformation zu verwenden:

public class ZoomApplication extends Application { 
    static public 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 scale = canvas.getScale(); // currently we only use Y, same value is used for X 
       double oldScale = scale; 

       scale *= Math.pow(1.01, event.getDeltaY()); 

       if (scale <= MIN_SCALE) { 
        scale = MIN_SCALE; 
       } else if (scale >= MAX_SCALE) { 
        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); 
       canvas.setPivot(f*dx, f*dy); 

       event.consume(); 

      } 

     }; 


    } 

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

    } 
} 

Nach einigen Gedanken über Zoom, kam ich zu dem Schluss, dass es wäre eine gute Idee

  1. schreibt eine unabhängige Zoom Hilfsmethode

    sein, um die Zoom-Funktion
  2. um auch die Unterstützung zu erleichtern Pinch-to-Zoom-Geste mit der gleichen Methode

Also schrieb ich die folgende Hilfsmethode:

/** Allow to zoom/scale any node with pivot at scene (x,y) coordinates. 
* 
* @param node 
* @param delta 
* @param x 
* @param y 
*/ 
public static void zoom(Node node, double factor, double x, double y) { 
    double oldScale = node.getScaleX(); 
    double scale = oldScale * factor; 
    if (scale < 0.05) scale = 0.05; 
    if (scale > 50) scale = 50; 
    node.setScaleX(scale); 
    node.setScaleY(scale); 

    double f = (scale/oldScale)-1; 
    Bounds bounds = node.localToScene(node.getBoundsInLocal()); 
    double dx = (x - (bounds.getWidth()/2 + bounds.getMinX())); 
    double dy = (y - (bounds.getHeight()/2 + bounds.getMinY())); 

    node.setTranslateX(node.getTranslateX()-f*dx); 
    node.setTranslateY(node.getTranslateY()-f*dy); 
} 

public static void zoom(Node node, ScrollEvent event) { 
    zoom(node, Math.pow(1.01, event.getDeltaY()), event.getSceneX(), event.getSceneY()); 
} 
public static void zoom(Node node, ZoomEvent event) { 
    zoom(node, event.getZoomFactor(), event.getSceneX(), event.getSceneY()); 
} 

mir erlaubt, Zoom-Funktion auf jedem Knoten so einfach wie zu registrieren:

myView.setOnScroll(event -> GUITools.zoom(myView, event)); // mouse scroll wheel zoom 
    myView.setOnZoom(event -> GUITools.zoom(myView, event)); // pinch to zoom 

und fertig ...

+0

Vielen Dank für die schnelle Lösung. Es funktioniert wie erwartet :-) – Roland

+0

Vielen Dank für das Update. Ich werde das definitiv benutzen. – Roland

+0

Scheint, dass das Delta sehr von den Mausradeinstellungen abhängt. Ich habe. G. ein Bildschirm mit sehr hoher Auflösung und daher ein hoher Mausrad-Scroll-Wert. Wenn ich Ihren Code verwende und auszoomen, erhalte ich sofort einen Bereich von ~ 100x100 Pixeln. Ich denke also, man muss den Multiplikator mit getDeltaX() und getDeltaY() betrachten oder ein festes Delta verwenden. – Roland

0

ich Ihre SceneGestures Klasse ändern, so funktioniert jetzt.

class SceneGestures { 
    private double oldx; 
    private double oldy; 
    double ttx=0; 
    double tty=0; 
    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; 

      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; 

      if (scale <= MIN_SCALE) { 
       scale = MIN_SCALE; 
      } else if (scale >= MAX_SCALE) { 
       scale = MAX_SCALE; 
      } 



      if (oldx==0){ 
      ttx=event.getSceneX() ; 
      tty=event.getSceneY() ; 
      }else{ 
      if (oldx!=event.getSceneX()){ 
      ttx=((event.getSceneX()+oldx)/2) ;  
      } 
      if (oldy!=event.getSceneY()){ 
      tty=((event.getSceneY()+oldy)/2);  
      } 
      } 
      // pivot value must be untransformed, i. e. without scaling 
      canvas.setPivot( 
        ((ttx- canvas.getBoundsInParent().getMinX())/oldScale), 
        ((tty- canvas.getBoundsInParent().getMinY())/oldScale) 
        ); 
     // if (oldx==0){ 
      oldx=event.getSceneX(); 
      oldy=event.getSceneY(); 
     // } 
      //try { 
      // Robot rbt=new Robot(); 
      // rbt.mouseMove(512, 384); 
      //} catch (AWTException ex) { 
      // System.out.println(ex.getMessage()); 
      // } 

      canvas.setScale(scale); 


      System.out.println("new pivot x: " + canvas.scaleTransform.getPivotX() + "/" + canvas.scaleTransform.getPivotY() + ", new scale: " + scale); 
      System.out.println("bounds: " + canvas.getBoundsInParent());  
      System.out.println("old: " + oldx+" "+oldy); 
      System.out.println("tt: " + ttx+" "+tty); 
      event.consume(); 

     } 

    }; 


} 
+0

Funktioniert nicht, wenn Sie in den Kreis zoomen, auf das Rechteck schwenken und weiter zoomen. Jens-Peters Lösung funktioniert. Aber ich möchte dir trotzdem für deine Mühe danken :-) – Roland

0

In FX8 können Sie tun

final Affine accumulatedScales = new Affine(); 
chart.getTransforms().add(accumulatedScales); 

chart.setOnScroll(new EventHandler<ScrollEvent>() { 
    @Override public void handle(ScrollEvent event) { 
     accumulatedScales.appendScale(scaleFactor, scaleFactor, event.getX(), event.getY()); 
    } 
}); 

und Sie sind fertig

+0

Es funktioniert nicht. Wenn ich e. G. Bewegen Sie den Mauszeiger über die Mitte des Kreises, der Kreis bewegt sich, wenn ich das Scrollrad verwende. – Roland

Verwandte Themen