Ваша проблема - простая логическая ошибка.
Вы сохраняете curMatrix
и обновляете ее каждый раз.Это означает, что все значения, которые вы передаете translateSelf
и scaleSelf
, относятся к предыдущим значениям.
Однако, когда вы делаете scale -= scaleFactor
или scale += scaleFactor
, scale
является абсолютным значением шкалы.
Поэтому, когда вы используете его позже в scaleSelf
, выбыстро установить огромное абсолютное значение шкалы, которое не будет уменьшаться до тех пор, пока оно не станет меньше 1
.
(1.1 * 1.2 * 1.3 * 1.4 * 1.5 * 1.6) => real scale is 5.8
и
(1.1 * 1.2 * 1.3 * 1.2 * 1.1 * 1) => real scale is 2.3
^-- click zoom-out
Так что все, что вам нужно исправить, этострока, которая устанавливает это scale
значение: вместо того, чтобы увеличивать это значение, всегда устанавливайте его на 1
основе.
scale = zoom === 'in' ? 1 + scaleFactor : 1 - scaleFactor ;
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var currMatrix = ctx.getTransform();
var plusBtn = document.getElementById("plus");
var minusBtn = document.getElementById("minus");
var infoP = document.getElementById("info");
var zoom;
var scale = 1;
const scaleFactor = 0.1;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setTransform(currMatrix)
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 100, 100);
}
draw()
function getDomPoint(event) {
const offSetCanvasLeft = canvas.getBoundingClientRect().left;
const offSetCanvasTop = canvas.getBoundingClientRect().top;
return {
x: event.pageX - offSetCanvasLeft,
y: event.pageY - offSetCanvasTop,
}
}
function domToCanvasPoint(point) {
const domPoint = new window.DOMPoint(point.x, point.y);
return domPoint.matrixTransform(currMatrix);
}
function updateScale() {
scale = zoom === 'in' ? 1 + scaleFactor : 1 - scaleFactor;
setDebugInfo(scale)
}
function zoomMatrixIntoPoint(point) {
currMatrix
.translateSelf(point.x, point.y)
.scaleSelf(scale, scale)
.translateSelf(-point.x, -point.y);
}
function setDebugInfo(msg) {
infoP.innerHTML = msg;
}
canvas.addEventListener('mousedown', function(event) {
const domPoint = getDomPoint(event);
const canvasPoint = domToCanvasPoint(domPoint);
updateScale();
zoomMatrixIntoPoint(canvasPoint);
draw()
}, false);
plusBtn.addEventListener('click', function(event) {
zoom = 'in'
setDebugInfo(`ZOOM IN with scale ${scale}`)
}, false);
minusBtn.addEventListener('click', function(event) {
zoom = 'out'
setDebugInfo(`ZOOM OUT with scale ${scale}`)
}, false);
<canvas id="myCanvas" width="300" height="300" style="border:1px solid black"></canvas>
<button id="plus">+</button>
<button id="minus">-</button>
<p id="info">info here!</p>
Также обратите внимание, что DOMMatrix # scale () принимает необязательные origin аргументы, которыепозволит вам избежать двух вызовов преобразования:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var currMatrix = ctx.getTransform();
var plusBtn = document.getElementById("plus");
var minusBtn = document.getElementById("minus");
var infoP = document.getElementById("info");
var zoom;
var scale = 1;
const scaleFactor = 0.1;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setTransform(currMatrix)
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 100, 100);
}
draw()
function getDomPoint(event) {
const offSetCanvasLeft = canvas.getBoundingClientRect().left;
const offSetCanvasTop = canvas.getBoundingClientRect().top;
return {
x: event.pageX - offSetCanvasLeft,
y: event.pageY - offSetCanvasTop,
}
}
function domToCanvasPoint(point) {
const domPoint = new window.DOMPoint(point.x, point.y);
return domPoint.matrixTransform(currMatrix);
}
function updateScale() {
scale = zoom === 'in' ? 1 + scaleFactor : 1 - scaleFactor;
setDebugInfo(scale)
}
function zoomMatrixIntoPoint(point) {
currMatrix
// scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
.scaleSelf(scale, scale, 1, point.x, point.y, 0)
}
function setDebugInfo(msg) {
infoP.innerHTML = msg;
}
canvas.addEventListener('mousedown', function(event) {
const domPoint = getDomPoint(event);
const canvasPoint = domToCanvasPoint(domPoint);
updateScale();
zoomMatrixIntoPoint(canvasPoint);
draw()
}, false);
plusBtn.addEventListener('click', function(event) {
zoom = 'in'
setDebugInfo(`ZOOM IN with scale ${scale}`)
}, false);
minusBtn.addEventListener('click', function(event) {
zoom = 'out'
setDebugInfo(`ZOOM OUT with scale ${scale}`)
}, false);
<canvas id="myCanvas" width="300" height="300" style="border:1px solid black"></canvas>
<button id="plus">+</button>
<button id="minus">-</button>
<p id="info">info here!</p>
И если вам нужно, чтобы все ваши значения были абсолютными (т.е. тоже переводились), то просто каждый раз создавайте новую DOMMatrix, и здесь сохраняйте исходное приращение масштаба:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var currMatrix = ctx.getTransform();
var plusBtn = document.getElementById("plus");
var minusBtn = document.getElementById("minus");
var infoP = document.getElementById("info");
var zoom;
var scale = 1;
const scaleFactor = 0.1;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setTransform(currMatrix)
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 100, 100);
}
draw()
function getDomPoint(event) {
const offSetCanvasLeft = canvas.getBoundingClientRect().left;
const offSetCanvasTop = canvas.getBoundingClientRect().top;
return {
x: event.pageX - offSetCanvasLeft,
y: event.pageY - offSetCanvasTop,
}
}
function domToCanvasPoint(point) {
const domPoint = new window.DOMPoint(point.x, point.y);
return domPoint.matrixTransform(currMatrix);
}
function updateScale() {
scale = zoom === 'in' ? scale + scaleFactor : scale - scaleFactor;
setDebugInfo(scale)
}
function zoomMatrixIntoPoint(point) {
// create a new DOMMatrix
currMatrix = new DOMMatrix()
.scaleSelf(scale, scale, 1, point.x, point.y, 0)
}
function setDebugInfo(msg) {
infoP.innerHTML = msg;
}
canvas.addEventListener('mousedown', function(event) {
const domPoint = getDomPoint(event);
const canvasPoint = domToCanvasPoint(domPoint);
updateScale();
zoomMatrixIntoPoint(canvasPoint);
draw()
}, false);
plusBtn.addEventListener('click', function(event) {
zoom = 'in'
setDebugInfo(`ZOOM IN with scale ${scale}`)
}, false);
minusBtn.addEventListener('click', function(event) {
zoom = 'out'
setDebugInfo(`ZOOM OUT with scale ${scale}`)
}, false);
<canvas id="myCanvas" width="300" height="300" style="border:1px solid black"></canvas>
<button id="plus">+</button>
<button id="minus">-</button>
<p id="info">info here!</p>