简体   繁体   中英

JAVAFX zoom, scroll in ScrollPane

I have JAVAFX application with zoom and scale as described here: Scale at pivot point in an already scaled node What I need to achieve is to place the image to right pane and keep the image on the left as depicted in the figure below. 在此输入图像描述

Question How can I embed this application into SPlitPane, where on left will be another panel.

    SplitPane splitPane = new SplitPane();
    splitPane.getItems().add(new Label("Left Panel"));
    splitPane.getItems().add(group);

    Scene scene = new Scene(splitPane, 1024, 768);

Unfortunately the code results in wrong coordinates,

For zooming inside a ScrollPane , you need to adjust the scroll positions. This is a bit more complex than just appending a transformation , which could be done, if no ScrollPane was used.

Basically you need to check the current scroll position before scaling the node and after scaling modify the scroll positions to prevent the pivot point from changing it's position because of the changed scale factor.

The following example lets you move a circle around "in" a grid and move around the grid when not clicking on a circle (pannable ScrollPane ) in addition to allowing you to adjust the zoom when moving the mouse wheel while holding down the CTRL key.

import java.net.MalformedURLException;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
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.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class PivotZoom extends Application {

    public static Region createContent() {
        double width = 1000;
        double height = 1000;

        Canvas canvas = new Canvas(width, height);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        gc.setFill(Color.LIGHTGREY);
        gc.fillRect(0, 0, width, height);

        gc.setStroke(Color.BLUE);
        gc.beginPath();

        for (int i = 50; i < width; i += 50) {
            gc.moveTo(i, 0);
            gc.lineTo(i, height);
        }

        for (int i = 50; i < height; i += 50) {
            gc.moveTo(0, i);
            gc.lineTo(width, i);
        }
        gc.stroke();

        Pane content = new Pane(
                new Circle(50, 50, 20),
                new Circle(120, 90, 20, Color.RED),
                new Circle(200, 70, 20, Color.GREEN)
        );

        StackPane result = new StackPane(canvas, content);
        result.setAlignment(Pos.TOP_LEFT);

        class DragData {

            double startX;
            double startY;
            double startLayoutX;
            double startLayoutY;
            Node dragTarget;
        }

        DragData dragData = new DragData();

        content.setOnMousePressed(evt -> {
            if (evt.getTarget() != content) {
                // initiate drag gesture, if a child of content receives the
                // event to prevent ScrollPane from panning.
                evt.consume();
                evt.setDragDetect(true);
            }
        });

        content.setOnDragDetected(evt -> {
            Node n = (Node) evt.getTarget();
            if (n != content) {
                // set start paremeters
                while (n.getParent() != content) {
                    n = n.getParent();
                }
                dragData.startX = evt.getX();
                dragData.startY = evt.getY();
                dragData.startLayoutX = n.getLayoutX();
                dragData.startLayoutY = n.getLayoutY();
                dragData.dragTarget = n;
                n.startFullDrag();
                evt.consume();
            }
        });

        // stop dragging when mouse is released
        content.setOnMouseReleased(evt -> dragData.dragTarget = null);

        content.setOnMouseDragged(evt -> {
            if (dragData.dragTarget != null) {
                // move dragged node
                dragData.dragTarget.setLayoutX(evt.getX() + dragData.startLayoutX - dragData.startX);
                dragData.dragTarget.setLayoutY(evt.getY() + dragData.startLayoutY - dragData.startY);
                Point2D p = new Point2D(evt.getX(), evt.getY());
                evt.consume();
            }
        });

        return result;
    }

    @Override
    public void start(Stage primaryStage) throws MalformedURLException {
        Region zoomTarget = createContent();
        zoomTarget.setPrefSize(1000, 1000);
        zoomTarget.setOnDragDetected(evt -> {
            Node target = (Node) evt.getTarget();
            while (target != zoomTarget && target != null) {
                target = target.getParent();
            }
            if (target != null) {
                target.startFullDrag();
            }
        });

        Group group = new Group(zoomTarget);

        // stackpane for centering the content, in case the ScrollPane viewport
        // is larget than zoomTarget
        StackPane content = new StackPane(group);
        group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
            // keep it at least as large as the content
            content.setMinWidth(newBounds.getWidth());
            content.setMinHeight(newBounds.getHeight());
        });

        ScrollPane scrollPane = new ScrollPane(content);
        scrollPane.setPannable(true);
        scrollPane.viewportBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
            // use vieport size, if not too small for zoomTarget
            content.setPrefSize(newBounds.getWidth(), newBounds.getHeight());
        });

        content.setOnScroll(evt -> {
            if (evt.isControlDown()) {
                evt.consume();

                final double zoomFactor = evt.getDeltaY() > 0 ? 1.2 : 1 / 1.2;

                Bounds groupBounds = group.getLayoutBounds();
                final Bounds viewportBounds = scrollPane.getViewportBounds();

                // calculate pixel offsets from [0, 1] range
                double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
                double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());

                // convert content coordinates to zoomTarget coordinates
                Point2D posInZoomTarget = zoomTarget.parentToLocal(group.parentToLocal(new Point2D(evt.getX(), evt.getY())));

                // calculate adjustment of scroll position (pixels)
                Point2D adjustment = zoomTarget.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));

                // do the resizing
                zoomTarget.setScaleX(zoomFactor * zoomTarget.getScaleX());
                zoomTarget.setScaleY(zoomFactor * zoomTarget.getScaleY());

                // refresh ScrollPane scroll positions & content bounds
                scrollPane.layout();

                // convert back to [0, 1] range
                // (too large/small values are automatically corrected by ScrollPane)
                groupBounds = group.getLayoutBounds();
                scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
                scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
            }
        });

        StackPane left = new StackPane(new Label("Left Menu"));
        SplitPane root = new SplitPane(left, scrollPane);

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

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

Try adding what it is you wish to zoom and scroll to a group layout. Then add the group to a scrollpane. This will keep the node you are zooming on the left position of the scrollpane. Hope this solves your problem!

Node you are zooming inside group, Group inside scrollpane, Scrollpane on the rightside of the Splitpane!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM