jake09
jake09

Reputation: 39

Zooming and Panning javaFX - change scroll

I have .fxml-file and Controller.java.

I've got this structure: AnchorPane -> BorderPane -> ImageView;

In this ImageView I have a possibility to zoom and pan the image (I've used code from stackoverflow:) ). But, unfortunately, I'm not very close with all that geometry and formulas, so I need to swap scroll (now zoom in works when I'm scrolling in) - I want to zoom in when I'm scrolling out.

Can you help me with that?

private void setImage(int imageId) {
    Image selectedImage = new ImageService().getObjectByID(DBResource.Image.SELECT_BY_ID,
            imageId);

    ImageView imageView = setImageView(selectedImage);

    //zoom
    double width = imageView.getImage().getWidth();
    double height = imageView.getImage().getHeight();

    imageView.setPreserveRatio(true);
    reset(imageView, width, height);

    ObjectProperty<Point2D> mouseDown = new SimpleObjectProperty<>();

    imageView.setOnMousePressed(e -> {

        Point2D mousePress = imageViewToImage(imageView, new Point2D(e.getX(), e.getY()));
        mouseDown.set(mousePress);
    });

    imageView.setOnMouseDragged(e -> {
        Point2D dragPoint = imageViewToImage(imageView, new Point2D(e.getX(), e.getY()));
        shift(imageView, dragPoint.subtract(mouseDown.get()));
        mouseDown.set(imageViewToImage(imageView, new Point2D(e.getX(), e.getY())));
    });

    imageView.setOnScroll(e -> {
        double delta = e.getDeltaY();
        Rectangle2D viewport = imageView.getViewport();

        double scale = clamp(Math.pow(1.01, delta),

                // don't scale so we're zoomed in to fewer than MIN_PIXELS in any direction:
                Math.min(MIN_PIXELS / viewport.getWidth(), MIN_PIXELS / viewport.getHeight()),

                // don't scale so that we're bigger than image dimensions:
                Math.max(width / viewport.getWidth(), height / viewport.getHeight())

        );

        Point2D mouse = imageViewToImage(imageView, new Point2D(e.getX(), e.getY()));

        double newWidth = viewport.getWidth() * scale;
        double newHeight = viewport.getHeight() * scale;

        // To keep the visual point under the mouse from moving, we need
        // (x - newViewportMinX) / (x - currentViewportMinX) = scale
        // where x is the mouse X coordinate in the image

        // solving this for newViewportMinX gives

        // newViewportMinX = x - (x - currentViewportMinX) * scale

        // we then clamp this value so the image never scrolls out
        // of the imageview:

        double newMinX = clamp(mouse.getX() - (mouse.getX() - viewport.getMinX()) * scale,
                0, width - newWidth);
        double newMinY = clamp(mouse.getY() - (mouse.getY() - viewport.getMinY()) * scale,
                0, height - newHeight);

        imageView.setViewport(new Rectangle2D(newMinX, newMinY, newWidth, newHeight));
    });

    imageView.setOnMouseClicked(e -> {
        if (e.getClickCount() == 2) {
            reset(imageView, width, height);
        }
    });

    imageView.setPreserveRatio(true);
    borderPane.setCenter(imageView);

    imageView.fitWidthProperty().bind(pane.widthProperty());
    imageView.fitHeightProperty().bind(pane.heightProperty());
}

// reset to the top left:
private void reset(ImageView imageView, double width, double height) {
    imageView.setViewport(new Rectangle2D(0, 0, width, height));
}

// shift the viewport of the imageView by the specified delta, clamping so
// the viewport does not move off the actual image:
private void shift(ImageView imageView, Point2D delta) {
    Rectangle2D viewport = imageView.getViewport();

    double width = imageView.getImage().getWidth() ;
    double height = imageView.getImage().getHeight() ;

    double maxX = width - viewport.getWidth();
    double maxY = height - viewport.getHeight();

    double minX = clamp(viewport.getMinX() - delta.getX(), 0, maxX);
    double minY = clamp(viewport.getMinY() - delta.getY(), 0, maxY);

    imageView.setViewport(new Rectangle2D(minX, minY, viewport.getWidth(), viewport.getHeight()));
}

private double clamp(double value, double min, double max) {

    if (value < min)
        return min;
    if (value > max)
        return max;
    return value;
}

// convert mouse coordinates in the imageView to coordinates in the actual image:
private Point2D imageViewToImage(ImageView imageView, Point2D imageViewCoordinates) {
    double xProportion = imageViewCoordinates.getX() / imageView.getBoundsInLocal().getWidth();
    double yProportion = imageViewCoordinates.getY() / imageView.getBoundsInLocal().getHeight();

    Rectangle2D viewport = imageView.getViewport();
    return new Point2D(
            viewport.getMinX() + xProportion * viewport.getWidth(),
            viewport.getMinY() + yProportion * viewport.getHeight());
}

private ImageView setImageView(Image image) {

    ImageView imageView = new ImageView();
    imageView.setImage(new javafx.scene.image.Image("file:" + image.getAbsPathI()));

    double w;
    double h;

    double ratioX = imageView.getFitWidth() / imageView.getImage().getWidth();
    double ratioY = imageView.getFitHeight() / imageView.getImage().getHeight();

    double reducCoeff;
    if(ratioX >= ratioY) {
        reducCoeff = ratioY;
    } else {
        reducCoeff = ratioX;
    }

    w = imageView.getImage().getWidth() * reducCoeff;
    h = imageView.getImage().getHeight() * reducCoeff;

    imageView.setX((imageView.getFitWidth() - w) / 2);
    imageView.setY((imageView.getFitHeight() - h) / 2);

    return imageView;
}

Upvotes: 2

Views: 2543

Answers (1)

DVarga
DVarga

Reputation: 21799

As I see in the example (and don't forget to credit James_D as this is his example) this line controls the zooming:

double delta = e.getDeltaY();

Therefore if you set it like ...

double delta = -e.getDeltaY();

... the zoom in/out will be switched.

Upvotes: 1

Related Questions