问题
I have a canvas, size 400 x 400. On it, i have drawn a map area, 200 x 200. I have translated this to the center of the canvas. I can zoom in and out, all is well. But, when i pan, it zooms from the center of my map area. I want it to always zoom from the center of the canvas no matter where the map area is. I think i need to negate the pan coords somehow, but i can't figure it out. Here is my code:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var canvas1 = document.getElementById("canvasX");
var ctxX = canvas1.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var mapW = 200;
var mapH = 200;
var panX=0;
var panY=0;
var scaleFactor=1.00;
drawTranslated();
function zoomIn(){
document.getElementById("zoomin").click();
{ scaleFactor*=1.1; drawTranslated(); };
}
function zoomOut(){
document.getElementById("zoomout").click();
{ scaleFactor/=1.1; drawTranslated(); };
}
function panUp(){
document.getElementById("panup").click();
{ panY-=25; drawTranslated(); };
}
function panDown(){
document.getElementById("pandown").click();
{ panY+=25; drawTranslated(); };
}
function panLeft(){
document.getElementById("panleft").click();
{ panX-=25; drawTranslated(); };
}
function panRight(){
document.getElementById("panright").click();
{ panX+=25; drawTranslated(); };
}
function drawTranslated(){
// canvas
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.translate(cw/2, ch/2);
ctx.translate(panX,panY);
ctx.scale(scaleFactor, scaleFactor);
ctx.fillStyle = "Green";
ctx.fillRect(mapW/-2, mapH/-2, mapW, mapH);
ctx.restore();
// canvasX
ctxX.clearRect(0,0,cw,ch);
ctxX.save();
ctxX.translate(cw/2, ch/2);
ctxX.beginPath();
ctxX.moveTo(0, 25);
ctxX.lineTo(0, -25);
ctxX.moveTo(-25, 0);
ctxX.lineTo(25, 0);
ctxX.closePath();
ctxX.lineWidth = 1;
ctxX.strokeStyle = 'Black';
ctxX.stroke();
ctxX.restore();
}
#wrapper {position: relative;}
canvas {position: absolute; border: 1px solid Black;}
<!DOCTYPE html>
<html>
<body>
<div id="wrapper">
<button id="zoomin" class="nav" title="Zoom In" onclick="zoomIn()">+</button>
<button id="zoomout" class="nav" title="Zoom Out" onclick="zoomOut()">−</button>
<button id="panup" class="nav" title="Up" onclick="panUp()">⇧</button>
<button id="pandown" class="nav" title="Down" onclick="panDown()">⇩</button>
<button id="panleft" class="nav" title="Left" onclick="panLeft()">⇦</button>
<button id="panright" class="nav" title="Right" onclick="panRight()">⇨</button>
</div>
<div id="wrapper">
<canvas id="myCanvas" width="400" height="400"></canvas>
<canvas id="canvasX" width="400" height="400"></canvas>
</div>
</body>
</html>
回答1:
After some time of trying to find some equation to solve this, i think i have found the solution.
ctx.translate(panX,panY);
ctx.scale(scaleFactor, scaleFactor);
By panning first, then scaling, it will zoom from the center of the context (In this case the green square). However, simply changing it around to:
ctx.scale(scaleFactor, scaleFactor);
ctx.translate(panX,panY);
it will zoom from the center of the canvas. It seems to do what i want it to, so unless i am mistaken, i believe this is the answer.
I have included another snippet. The only changes are those 2 lines, but i think it would be helpful for people to able to see the difference it makes.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas1 = document.getElementById("canvasX");
var ctxX = canvas1.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var mapW = 200;
var mapH = 200;
var panX = 0;
var panY = 0;
var scaleFactor = 1.00;
drawTranslated();
function zoomIn() {
document.getElementById("zoomin").click(); {
scaleFactor *= 1.1;
drawTranslated();
};
};
function zoomOut() {
document.getElementById("zoomout").click(); {
scaleFactor /= 1.1;
drawTranslated();
};
};
function panUp() {
document.getElementById("panup").click(); {
panY -= 25;
drawTranslated();
};
};
function panDown() {
document.getElementById("pandown").click(); {
panY += 25;
drawTranslated();
};
};
function panLeft() {
document.getElementById("panleft").click(); {
panX -= 25;
drawTranslated();
};
};
function panRight() {
document.getElementById("panright").click(); {
panX += 25;
drawTranslated();
};
};
function drawTranslated() {
// canvas
ctx.clearRect(0, 0, cw, ch);
ctx.save();
ctx.translate(cw / 2, ch / 2);
ctx.scale(scaleFactor, scaleFactor);
ctx.translate(panX, panY);
ctx.fillStyle = "Green";
ctx.fillRect(mapW / -2, mapH / -2, mapW, mapH);
ctx.restore();
// canvasX
ctxX.clearRect(0, 0, cw, ch);
ctxX.save();
ctxX.translate(cw / 2, ch / 2);
ctxX.beginPath();
ctxX.moveTo(0, 25);
ctxX.lineTo(0, -25);
ctxX.moveTo(-25, 0);
ctxX.lineTo(25, 0);
ctxX.closePath();
ctxX.lineWidth = 1;
ctxX.strokeStyle = 'Black';
ctxX.stroke();
ctxX.restore();
};
#wrapper {
position: relative;
}
canvas {
position: absolute;
border: 1px solid Black;
}
<div id="wrapper">
<button id="zoomin" class="nav" title="Zoom In" onclick="zoomIn()">+</button>
<button id="zoomout" class="nav" title="Zoom Out" onclick="zoomOut()">−</button>
<button id="panup" class="nav" title="Up" onclick="panUp()">⇧</button>
<button id="pandown" class="nav" title="Down" onclick="panDown()">⇩</button>
<button id="panleft" class="nav" title="Left" onclick="panLeft()">⇦</button>
<button id="panright" class="nav" title="Right" onclick="panRight()">⇨</button>
</div>
<div id="wrapper">
<canvas id="canvas" width="400" height="400"></canvas>
<canvas id="canvasX" width="400" height="400"></canvas>
</div>
回答2:
There surely is a better way but in Fabric.js you can do
canvas.zoomToPoint(new fabric.Point(canvas.width / 2, canvas.height / 2), canvas.getZoom() / ZOOM_PERCENT);
See more here Canvas
For a more pure solution, you can also try translating the context by half the canvas size using
ctx.translate()
A fiddle that might help
http://jsfiddle.net/m1erickson/QEuw4/
来源:https://stackoverflow.com/questions/39070414/how-to-zoom-to-center-of-canvas-not-to-center-of-context