In your case, scaling should be applied not to the object of the Сanvas class, but to the image that is drawn on the graphic context ( GraphicContext ).
You can either change all coordinates used when drawing, or set a scaling transformation before drawing using the setTransform() method (setting the transform property of the graphics context).
Using the second method, I designed it as an inheritor of the Canvas class and got the following draft:
// 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(); } }