JavaFx, Зум “рисунка” в canvas

270
02 апреля 2018, 23:53

Есть такой код для canvas, что бы при прокрутке колесиком мышки, можно было увеличивать или уменьшать содержание canvas. Но есть одно но, при увеличении оно просто увеличивает сам Сanvas выходя за пределы допустимого окна, налаживаясь на другие части интерфейса. Как можно это решить? или каким образом сделать адекватный зум?

 myCanvas.setOnScroll((event) ->
     double x = myCanvas.getScaleX();
     double y = myCanvas.getScaleY();
     if (x <= 0.8 && y <= 0.8) {
         x = 0.8;
         y = 0.8;
     }
     if (x >= 2 && y >= 2) {
         x = 2;
         y = 2;
     }
     myCanvas.setScaleX(x + event.getDeltaY() / 800);
     myCanvas.setScaleY(y + event.getDeltaY() / 800);
    });
Answer 1

В вашем случае масштабирование следует применять не на объект класса Сanvas, а на изображение, которое рисуется на графическом контексте (GraphicContext).

Можно либо изменять все координаты, используемые при рисовании, либо перед рисованием установить преобразование масштабирования с помощью метода setTransform() (установка свойства transform графического контекста).

Используя второй метод, оформил это в виде наследника класса Canvas и получил такой черновой вариант:

// ZoomableCanvas.java:

package example;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.ScrollEvent;
import javafx.scene.transform.Affine;
public abstract class ZoomableCanvas extends Canvas {
    public ZoomableCanvas() {
        this(0, 0);
    }
    public ZoomableCanvas(double width, double height) {
        super(width, height);
        this.setOnScroll(zoomHandler);
        this.zoomProperty().addListener(o -> redraw());
    }
    static protected EventHandler<ScrollEvent> zoomHandler = new EventHandler<ScrollEvent>() {
        @Override
        public void handle(ScrollEvent event) {
            ZoomableCanvas zcanvas = (ZoomableCanvas) event.getSource();
            GraphicsContext gc = zcanvas.getGraphicsContext2D();
            Affine affine = gc.getTransform();
            double zoom = affine.getMxx() + event.getDeltaY() / 800;
            if (zoom <= 0.8) {
                zoom = 0.8;
            }
            if (zoom >= 2.0) {
                zoom = 2.0;
            }
            zcanvas.setZoom(zoom);
            zcanvas.redraw();
        }
    };
    private SimpleDoubleProperty zoom = new SimpleDoubleProperty(1.0);
    public void setZoom(double value) {
        if (value != getZoom()) {
            this.zoom.set(value);
            redraw();
        }
    }
    public double getZoom() {
        return zoom.get();
    }
    public DoubleProperty zoomProperty() {
        return zoom;
    }
    public void clean() {
        GraphicsContext gc = this.getGraphicsContext2D();
        Canvas canvas = gc.getCanvas();
        Affine affine = gc.getTransform();
        gc.setTransform(1, 0, 0, 1, 0, 0);
        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
        gc.setTransform(affine);
    }
    public void redraw() {
        GraphicsContext gc = this.getGraphicsContext2D();
        if (gc == null) return;
        // Чистим:
        Canvas canvas = gc.getCanvas();
        double w = canvas.getWidth();
        double h = canvas.getHeight();
        gc.setTransform(1, 0, 0, 1, 0, 0);
        gc.clearRect(0, 0, w, h);
        double z = getZoom();
        // Масштабирование в левый верхний угол:
        //gc.setTransform(z, 0, 0, z, 0, 0);
        // Масштабирование в центр canvas:
        gc.setTransform(z, 0, 0, z, (w - w * z) / 2.0, (h - h * z) / 2.0);
        // Рисуем:
        paint(gc);
    }
    public abstract void paint(GraphicsContext gc);
}

// Main.java:

package example;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) {
        ZoomableCanvas canvas = new ZoomableCanvas(300, 200) {
            @Override
            public void paint(GraphicsContext gc) {
                // Рисуем:
                gc.setFill(Color.LIGHTGREEN);
                gc.fillOval(60, 10, 180, 180);
                gc.setFill(Color.WHITE);
                gc.fillOval(100, 50, 100, 100);
            }
        };
        Label zoomLabel = new Label();
        zoomLabel.textProperty().bind(canvas.zoomProperty().asString());
        Slider slider = new Slider(0.8, 2.0, 1.0);
        slider.valueProperty().bindBidirectional(canvas.zoomProperty());
        VBox root = new VBox(zoomLabel, canvas, slider);
        root.setPadding(new Insets(15));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
        canvas.redraw();
    }
}
READ ALSO
Java проблема парсинга даты

Java проблема парсинга даты

Решаю задачу парсинга вакансий с сайта HeadeHunter при помощи jsoup и пытаюсь парсить дату, но получаю ошибку:

292
проблемы с реализацией LinkedList [требует правки]

проблемы с реализацией LinkedList [требует правки]

Нужно получить значение 3 в результате выполнения следующего кода: Systemout

185
AndroidStudio не работает(Windows)

AndroidStudio не работает(Windows)

Доброго времени суток!Я установил андроид студио и ява,но установить сдк не получается точнее интегрировать его в андроид сдкЯ нажимаю установить,он...

224
Проверка наличия ключа в Бд Firebase

Проверка наличия ключа в Бд Firebase

Делаю проверку таким образом

166