我试图在画布上创建缩放效果,而且我设法做到了,但这是一个小问题。缩放(缩放)原点位于画布的左上角。如何指定缩放/缩放原点?
我想我需要使用translate
,但我不知道应该如何以及在哪里实施它。
我想用作缩放原点的是鼠标位置,但为简单起见,画布的中心可以。
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 400;
var global = {
zoom: {
origin: {
x: null,
y: null,
},
scale: 1,
},
};
function zoomed(number) {
return Math.floor(number * global.zoom.scale);
}
function draw() {
context.beginPath();
context.rect(zoomed(50), zoomed(50), zoomed(100), zoomed(100));
context.fillStyle = 'skyblue';
context.fill();
context.beginPath();
context.arc(zoomed(350), zoomed(250), zoomed(50), 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
}
draw();
canvas.addEventListener("wheel", trackWheel);
canvas.addEventListener("wheel", zoom);
function zoom() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
draw();
}
function trackWheel(e) {
if (e.deltaY < 0) {
if (global.zoom.scale < 5) {
global.zoom.scale *= 1.1;
}
} else {
if (global.zoom.scale > 0.1) {
global.zoom.scale *= 0.9;
}
}
global.zoom.scale = parseFloat(global.zoom.scale.toFixed(2));
}
&#13;
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
&#13;
<canvas id="canvas"></canvas>
&#13;
<小时/> 更新1
似乎在SO上与此主题相关的其他问题很少,但我无法直接在我的代码中实现。
我试图检查demo中提供的Zoom Canvas to Mouse Cursor Phrogz,但它太复杂了(至少对我而言)。试图实施他的解决方案:
ctx.translate(pt.x,pt.y);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 400;
var global = {
zoom: {
origin: {
x: null,
y: null,
},
scale: 1,
},
};
function draw() {
context.beginPath();
context.rect(50, 50, 100, 100);
context.fillStyle = 'skyblue';
context.fill();
context.beginPath();
context.arc(350, 250, 50, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
}
draw();
canvas.addEventListener("wheel", trackWheel);
canvas.addEventListener("wheel", trackMouse);
canvas.addEventListener("wheel", zoom);
function zoom() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(global.zoom.origin.x, global.zoom.origin.y);
context.scale(global.zoom.scale, global.zoom.scale);
context.translate(-global.zoom.origin.x, -global.zoom.origin.y);
draw();
}
function trackWheel(e) {
if (e.deltaY > 0) {
if (global.zoom.scale > 0.1) {
global.zoom.scale *= 0.9;
}
} else {
if (global.zoom.scale < 5) {
global.zoom.scale *= 1.1;
}
}
global.zoom.scale = parseFloat(global.zoom.scale.toFixed(2));
}
function trackMouse(e) {
global.zoom.origin.x = e.clientX;
global.zoom.origin.y = e.clientY;
}
&#13;
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
&#13;
<canvas id="canvas"></canvas>
&#13;
但它并没有真正帮助。它似乎使用鼠标位置作为缩放原点,但有&#34;跳跃&#34;当我放大。
更新2
我已经成功地从Blindman67的示例中分离并简化了缩放效果,以了解它如何更好地工作。我必须承认,我还是不完全理解它:)我将在这里分享它。未来的访客可能会受益。
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 400;
var zoom = {
scale : 1,
screen : {
x : 0,
y : 0,
},
world : {
x : 0,
y : 0,
},
};
var mouse = {
screen : {
x : 0,
y : 0,
},
world : {
x : 0,
y : 0,
},
};
var scale = {
length : function(number) {
return Math.floor(number * zoom.scale);
},
x : function(number) {
return Math.floor((number - zoom.world.x) * zoom.scale + zoom.screen.x);
},
y : function(number) {
return Math.floor((number - zoom.world.y) * zoom.scale + zoom.screen.y);
},
x_INV : function(number) {
return Math.floor((number - zoom.screen.x) * (1 / zoom.scale) + zoom.world.x);
},
y_INV : function(number) {
return Math.floor((number - zoom.screen.y) * (1 / zoom.scale) + zoom.world.y);
},
};
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.rect(scale.x(50), scale.y(50), scale.length(100), scale.length(100));
context.fillStyle = 'skyblue';
context.fill();
context.beginPath();
context.arc(scale.x(350), scale.y(250), scale.length(50), 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
}
canvas.addEventListener("wheel", zoomUsingCustomScale);
function zoomUsingCustomScale(e) {
trackMouse(e);
trackWheel(e);
scaleShapes();
}
function trackMouse(e) {
mouse.screen.x = e.clientX;
mouse.screen.y = e.clientY;
mouse.world.x = scale.x_INV(mouse.screen.x);
mouse.world.y = scale.y_INV(mouse.screen.y);
}
function trackWheel(e) {
if (e.deltaY < 0) {
zoom.scale = Math.min(5, zoom.scale * 1.1);
} else {
zoom.scale = Math.max(0.1, zoom.scale * (1/1.1));
}
}
function scaleShapes() {
zoom.screen.x = mouse.screen.x;
zoom.screen.y = mouse.screen.y;
zoom.world.x = mouse.world.x;
zoom.world.y = mouse.world.y;
mouse.world.x = scale.x_INV(mouse.screen.x);
mouse.world.y = scale.y_INV(mouse.screen.y);
draw();
}
draw();
&#13;
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
&#13;
<canvas id="canvas"></canvas>
&#13;
由于这是一个简化版本,我建议您首先查看Blindman67的示例。此外,即使我已经接受了Blindman67的答案,你仍然可以发布答案。我发现这个主题很有趣。所以我想更多地了解它。
答案 0 :(得分:9)
如果它只是缩放和平移,则解决方案很简单。
您需要跟踪两个来源。一个是鼠标在世界坐标中的位置(方框和圆位),另一个是鼠标在屏幕坐标中的位置(画布像素)
您需要习惯从一个坐标系转换到另一个坐标系。这是通过反函数完成的。从世界坐标到屏幕坐标可以通过反转功能反转,从屏幕坐标转换为世界坐标。
反转一些简单函数的例子
2 * 10 = 20
反之为20 / 10 = 2
2 + 3 = 5
反之为5 - 3 = 2
(3 - 1) * 5 = 10
反之为10 * (1/5) + 1 = 3
乘以* 1。即x*5
变为x * 1/5
(或仅x/5
)
添加成为减法,减去成为添加,首先成为最后一个,最后成为第一个(3 - first) * last = result
反向为result / last + first = 3
因此,您缩放坐标(框的世界坐标位置)并获取框的屏幕位置(以像素为单位)。如果您想要屏幕像素的世界坐标,则应用反向
这一切都满口所以这里是你的代码做你需要的一些评论和我添加了mousemove,按钮的东西和其他的东西,因为你需要鼠标pos和缩放没有点,如果你不能平移,需要停止鼠标按钮锁定和停止滚动滚动等等...要平移移动世界原点(在代码中)单击在UI中拖动。此外,我很懒,现在摆脱了global.zoom.origin.x
的东西scale
你知道的很多,wx,wy
,sx,sy
是源代码的读代码。
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 400;
// lazy programmers globals
var scale = 1;
var wx = 0; // world zoom origin
var wy = 0;
var sx = 0; // mouse screen pos
var sy = 0;
var mouse = {};
mouse.x = 0; // pixel pos of mouse
mouse.y = 0;
mouse.rx = 0; // mouse real (world) pos
mouse.ry = 0;
mouse.button = 0;
function zoomed(number) { // just scale
return Math.floor(number * scale);
}
// converts from world coord to screen pixel coord
function zoomedX(number) { // scale & origin X
return Math.floor((number - wx) * scale + sx);
}
function zoomedY(number) { // scale & origin Y
return Math.floor((number - wy) * scale + sy);
}
// Inverse does the reverse of a calculation. Like (3 - 1) * 5 = 10 the inverse is 10 * (1/5) + 1 = 3
// multiply become 1 over ie *5 becomes * 1/5 (or just /5)
// Adds become subtracts and subtract become add.
// and what is first become last and the other way round.
// inverse function converts from screen pixel coord to world coord
function zoomedX_INV(number) { // scale & origin INV
return Math.floor((number - sx) * (1 / scale) + wx);
// or return Math.floor((number - sx) / scale + wx);
}
function zoomedY_INV(number) { // scale & origin INV
return Math.floor((number - sy) * (1 / scale) + wy);
// or return Math.floor((number - sy) / scale + wy);
}
// draw everything in pixels coords
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.rect(zoomedX(50), zoomedY(50), zoomed(100), zoomed(100));
context.fillStyle = 'skyblue';
context.fill();
context.beginPath();
context.arc(zoomedX(350), zoomedY(250), zoomed(50), 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
}
canvas.addEventListener("wheel", trackWheel);
canvas.addEventListener("mousemove", move)
canvas.addEventListener("mousedown", move)
canvas.addEventListener("mouseup", move)
canvas.addEventListener("mouseout", move) // to stop mouse button locking up
function move(event) { // mouse move event
if (event.type === "mousedown") {
mouse.button = 1;
}
else if (event.type === "mouseup" || event.type === "mouseout") {
mouse.button = 0;
}
mouse.bounds = canvas.getBoundingClientRect();
mouse.x = event.clientX - mouse.bounds.left;
mouse.y = event.clientY - mouse.bounds.top;
var xx = mouse.rx; // get last real world pos of mouse
var yy = mouse.ry;
mouse.rx = zoomedX_INV(mouse.x); // get the mouse real world pos via inverse scale and translate
mouse.ry = zoomedY_INV(mouse.y);
if (mouse.button === 1) { // is mouse button down
wx -= mouse.rx - xx; // move the world origin by the distance
// moved in world coords
wy -= mouse.ry - yy;
// recaculate mouse world
mouse.rx = zoomedX_INV(mouse.x);
mouse.ry = zoomedY_INV(mouse.y);
}
draw();
}
function trackWheel(e) {
e.preventDefault(); // stop the page scrolling
if (e.deltaY < 0) {
scale = Math.min(5, scale * 1.1); // zoom in
} else {
scale = Math.max(0.1, scale * (1 / 1.1)); // zoom out is inverse of zoom in
}
wx = mouse.rx; // set world origin
wy = mouse.ry;
sx = mouse.x; // set screen origin
sy = mouse.y;
mouse.rx = zoomedX_INV(mouse.x); // recalc mouse world (real) pos
mouse.ry = zoomedY_INV(mouse.y);
draw();
}
draw();
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
<canvas id="canvas"></canvas>