Zoom and pan to mouse position on wheel event?

I guys,

I’m fairly new to Phaser and still playing around a bit.

I have come so far to create a world map that gets data from a map created in Tiled and tiles and objects are working as it should.

I have also created a method to handle panning around the world map on mousedown and also a zoom method on wheel event.

However, i’m having some issues figuring out how to properly calculate how I keep zooming in on the same position on the map.

For example when I place my cursor on an island on the map and start zooming, it doesn’t zoom in on the island, but panning to the right of the viewport, if that makes sense.

This is my code:

in the create() method I have this for the “wheel” event:

this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
    this.handleZoom(pointer, deltaX, deltaY)
});

The handleZoom method:

    handleZoom(pointer, deltaX, deltaY) {
        const zoomDirection = deltaY > 0 ? -1 : 1;

        let newZoom = Phaser.Math.Clamp(
            this.cameras.main.zoom + zoomDirection * this.zoomState.zoomStep,
            this.zoomState.minZoom,
            this.zoomState.maxZoom
        );

        this.cameras.main.setZoom(newZoom);
    }

I’ve have tried adding this to the handleZoom method:

this.cameras.main.pan(pointer.worldX, pointer.worldY) 

But that doesn’t seem to do the trick.

Does anyone know the trick to do this properly?

Thanks! :slight_smile:

Try

this.cameras.main
  .setZoom(newZoom)
  .centerOn(pointer.worldX, pointer.worldY);

If you have camera bounds set those will affect it.

1 Like

Hi @samme

Thanks for your reply! Didn’t think about method chaining, but your solution didn’t work. The zooming just pans slightly to the right from where i’m pointing. Always to the same location.

I do however call a updateCameraBounds() in create(). If I remove this, the screen goes black when zooming in.

This is the snippet:

    updateCameraBounds() {
        const canvasWidth = this.scale.width;
        const canvasHeight = this.scale.height;
      
        this.cameras.main.setBounds(
          0, 
          0, 
          Math.max(this.worldWidth, canvasWidth), 
          Math.max(this.worldHeight, canvasHeight)
        );
    }

I went ahead and made a codepen for people to see what I mean :slight_smile:

I feel like @Antriel had something for this.

1 Like

Fingers crossed!

Without testing and checking if it even applies, here goes, code taken from 5 years old repository:

function zoomBy(zoomCoef:Float, x:Float, y:Float):Void {
    var worldPoint = scene.cameras.main.getWorldPoint(x, y);
    scene.cameras.main.zoom -= scene.cameras.main.zoom * 0.1 * zoomCoef;
    if (scene.cameras.main.zoom < 0.25) scene.cameras.main.zoom = 0.25;
    else if (scene.cameras.main.zoom > 2) scene.cameras.main.zoom = 2;

    scene.cameras.main.preRender(1); // update matrix
    var newWorldPoint = scene.cameras.main.getWorldPoint(x, y);
    scene.cameras.main.scrollX -= newWorldPoint.x - worldPoint.x;
    scene.cameras.main.scrollY -= newWorldPoint.y - worldPoint.y;
}

Not sure if it still applies, as it was written for very old Phaser 3 version, but the logic should work. That’s how NextRealm HexSweep – Multiplayer Online Hexagonal Minesweeper does zooming.

Thanks for the suggestion, @Antriel ! Unfortunately it didn’t work for me. I even tried to rewrite it to be up to date with version 3.60, but as soon as I scroll, the map just goes black for me.

This is the code that I ended up with after some rewrites (I also tried your code with very few changes to fit into my code):

    handleZoom(zoomCoef, x, y) {
        var worldPoint = this.cameras.main.getWorldPoint(x, y);
        this.cameras.main.setZoom(this.cameras.main.zoom * (1 - 0.1 * zoomCoef));
        if (this.cameras.main.zoom < 0.25){
            this.cameras.main.setZoom(0.25);
        } else if (this.cameras.main.zoom > 2){
            this.cameras.main.setZoom(2);
        } 
    
        this.cameras.main.preRender(1); // update matrix
        var newWorldPoint = this.cameras.main.getWorldPoint(x, y);
        this.cameras.main.setScroll(newWorldPoint.x - worldPoint.x, newWorldPoint.y - worldPoint.y);
    }

I have a feeling that i’m close.

I repainted and polished the code, plugged it into a working example and opened a pull request. See the code here: https://github.com/Antriel/phaser3-examples/blob/master/public/src/tilemap/mouse%20wheel%20zoom.js

1 Like

Thanks! I will try it later tonight when i’m at my computer :slight_smile:

Dude, this is perfect! Thanks for the help! And thanks, @samme, for the suggestion! :slight_smile: