Как сделать так, чтобы scale приближал картинку к курсору в канвасе JS?

const image = {
    object: new Image(),
    x: 0,
    y: 0,
    scale: 1,
    scaleMultiplier: 0.9
}

function draw() {
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    ctx.save();
    ctx.translate(image.x, image.y);
    ctx.scale(image.scale, image.scale);

    const imageWidth = image.object.width;
    const imageHeight = image.object.height;
    const centerCoordsX = (canvasWidth - imageWidth) / 2;
    const centerCoordsY = (canvasHeight - imageHeight) / 2;
    const t1 = (centerCoordsX + image.x) / (image.scale * 2);
    const t2 = (centerCoordsY + image.y) / (image.scale * 2);

    ctx.drawImage(image.object, t1, t2, imageWidth, imageHeight);
    ctx.fill();
    ctx.restore();
}

canvas.onwheel = function(e) {
    if (e.deltaY < 0)
        image.scale /= image.scaleMultiplier;
    else
        image.scale *= image.scaleMultiplier;
    draw();
};

При прокрутке колеса мыши сейчас картинка скейлится к верхнему левому углу, а как сделать так, чтобы она скейлилась к координатам курсора?


Ответы (1 шт):

Автор решения: dotagenius

Вот тут ответили на мой вопрос. Мне нужно было то же самое, но только с картинкой. Еще немного упростил код.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
const image = new Image();
image.src = 'https://picsum.photos/400';

requestAnimationFrame(drawCanvas);

let matrix = [1, 0, 0, 1, 0, 0];
let scale = 1;
const pos = { x: 0, y: 0 };
let dirty = true;
const mouse = {x: 0, y: 0, oldX: 0, oldY: 0, dragging: false};

canvas.addEventListener("mousemove", mouseEvent, {passive: true});
canvas.addEventListener("mousedown", mouseEvent, {passive: true});
canvas.addEventListener("mouseup", mouseEvent, {passive: true});
canvas.addEventListener("mouseout", mouseEvent, {passive: true});
canvas.addEventListener("wheel", mouseWheelEvent, {passive: false});

function apply() {
    if (dirty)
        update();
    ctx.setTransform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
}

function update() {
    dirty = false;
    matrix[3] = matrix[0] = scale;
    matrix[2] = matrix[1] = 0;
    matrix[4] = pos.x;
    matrix[5] = pos.y;
}

function pan(amount) {
    if (dirty)
        update();
    pos.x += amount.x;
    pos.y += amount.y;
    dirty = true;
}

function scaleAt(at, amount) {
    if (dirty)
        update();
    scale *= amount;
    pos.x = at.x - (at.x - pos.x) * amount;
    pos.y = at.y - (at.y - pos.y) * amount;
    dirty = true;
}

function drawCanvas() {
    if (dirty) {
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        apply();
        ctx.drawImage(image, 0, 0);
    }
    requestAnimationFrame(drawCanvas);
}

function mouseEvent(e) {
    if (e.type === "mousedown")
        mouse.dragging = true;
    if (e.type === "mouseup" || e.type === "mouseout")
        mouse.dragging = false;
    mouse.oldX = mouse.x;
    mouse.oldY = mouse.y;
    mouse.x = e.offsetX;
    mouse.y = e.offsetY
    if (mouse.dragging)
        pan({x: mouse.x - mouse.oldX, y: mouse.y - mouse.oldY});
}

function mouseWheelEvent(e) {
    let x = e.offsetX;
    let y = e.offsetY;
    if (e.deltaY < 0)
        scaleAt({x, y}, 1.1);
    else
        scaleAt({x, y}, 1 / 1.1);
    e.preventDefault();
}
canvas {
    width: 300px;
    height: 300px;
}
<canvas id="canvas" width="300px" height="300px"></canvas>

→ Ссылка