【发布时间】:2016-12-13 02:37:22
【问题描述】:
我正在尝试在画布上创建缩放效果,并且我已经设法做到了,但是有一个小问题。缩放(缩放)原点位于画布的左上角。如何指定缩放/缩放原点?
我想我需要使用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));
}
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
<canvas id="canvas"></canvas>
更新 1
似乎在 SO 上与此主题相关的其他问题很少,但我无法直接在我的代码中实现。
我试图检查Zoom Canvas to Mouse Cursor 中提供的demo 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;
}
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
<canvas id="canvas"></canvas>
但这并没有真正帮助。它似乎使用鼠标位置作为缩放原点,但是当我放大时有“跳跃”。
更新 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();
body {
background: gainsboro;
margin: 0;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
<canvas id="canvas"></canvas>
由于这是一个简化版本,我建议您先查看 Blindman67 的示例。此外,即使我接受了 Blindman67 的回答,您仍然可以发布答案。我觉得这个主题很有趣。所以我想了解更多。
【问题讨论】:
-
看看这个之前的回答here。
-
@MonicaOlejniczak 是的,已经看到了。当您发表评论时,我正在更新问题。他的演示太复杂,无法完美实现他的解决方案。
-
在放大/缩小时,如果您将画布缓存到内存中的第二个画布并使用该第二个画布作为源在主画布上进行绘制,您将获得更好的性能。不缩放时,您直接使用主画布进行绘图。这是之前的Q&A,展示了如何放大鼠标位置。
-
@markE 这仍然适用于动画画布吗?我最终会缩放画布动画。只是在尝试atm。只要缩放原点固定,更新后的代码似乎可以很好地放大/缩小。缩放原点(鼠标指针位置)的微小变化会导致“跳跃”。 Phrogz 的演示似乎没有这个问题。
-
缩放固定画布内容(使用第二个内存画布),是的。缩放不断变化的画布内容——谁知道呢,你必须测试你的应用才能看到它的行为。
标签: javascript animation canvas zooming