我正在尝试使用p5.js实现缩放功能。当前缩放级别以及x和y位置存储在controls.view
对象中。默认或(0,0)位置位于左上角。问题是在放大/缩小时调整x和y位置值,这样无论视图的当前位置是什么,它都将保持在缩放点或鼠标光标处。
缩放和翻译在draw
函数中通过x,y和缩放值执行,因此必须直接修改这些值(不能直接在画布上运行转换)。这是Codepen。
let canvas, circles;
const controls = {
view: {
x: 0,
y: 0,
zoom: 1
},
viewPos: {
prevX: null,
prevY: null,
isDragging: false
},
}
function setup() {
canvas = createCanvas(window.innerWidth, window.innerHeight);
canvas.mouseWheel(e => Controls.zoom(controls).worldZoom(e))
circles = Circle.create(100)
}
function draw() {
background(100)
translate(controls.view.x, controls.view.y);
scale(controls.view.zoom)
circles.forEach(circle => circle.show());
}
window.mousePressed = e => Controls.move(controls).mousePressed(e)
window.mouseDragged = e => Controls.move(controls).mouseDragged(e);
window.mouseReleased = e => Controls.move(controls).mouseReleased(e)
class Controls {
static move(controls) {
function mousePressed(e) {
controls.viewPos.isDragging = true;
controls.viewPos.prevX = e.clientX;
controls.viewPos.prevY = e.clientY;
}
function mouseDragged(e) {
const {
prevX,
prevY,
isDragging
} = controls.viewPos;
if (!isDragging) return;
const pos = {
x: e.clientX,
y: e.clientY
};
const dx = pos.x - prevX;
const dy = pos.y - prevY;
if (prevX || prevY) {
controls.view.x += dx;
controls.view.y += dy;
controls.viewPos.prevX = pos.x, controls.viewPos.prevY = pos.y
}
}
function mouseReleased(e) {
controls.viewPos.isDragging = false;
controls.viewPos.prevX = null;
controls.viewPos.prevY = null;
}
return {
mousePressed,
mouseDragged,
mouseReleased
}
}
static zoom(controls) {
// function calcPos(x, y, zoom) {
// const newX = width - (width * zoom - x);
// const newY = height - (height * zoom - y);
// return {x: newX, y: newY}
// }
function worldZoom(e) {
const {
x,
y,
deltaY
} = e;
const direction = deltaY > 0 ? -1 : 1;
const factor = 0.1;
const zoom = 1 * direction * factor;
controls.view.zoom += zoom;
controls.view.x += -(x * direction * factor);
controls.view.y += -(y * direction * factor);
}
return {
worldZoom
}
}
}
class Circle {
constructor(x, y) {
this.x = x;
this.y = y;
}
show() {
fill(255);
noStroke();
ellipse(this.x, this.y, 15, 15);
}
static create(count) {
return Array.from(Array(count), () => {
const x = random(-500, width + 500);
const y = random(-500, height + 500);
return new this(x, y);
})
}
}
body {margin: 0; padding: 0;}
canvas {vertical-align: top;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.min.js"></script>
function worldZoom(e) {
const {x, y, deltaY} = e;
const direction = deltaY > 0 ? -1 : 1;
const factor = 0.1;
const zoom = 1 * direction * factor;
controls.view.zoom += zoom;
controls.view.x += -(x * direction * factor);
controls.view.y += -(y * direction * factor);
}
这是我尝试实施的一些类似问题: Zoom Canvas to Mouse Cursor, Zoom in on a point (using scale and translate)
答案 0 :(得分:4)
在我看来,缩放的工作方式是基于感兴趣点的位置(在您的情况下是鼠标),缩放对画布的(x, y)
位置有加权影响。如果鼠标位于左上角,则此权重应为(0,0)
,如果它位于右下角,则应为(1,1)
。中心应为(0.5,0.5)
。
因此,假设我们有权重,每当缩放发生变化时,权重会向我们显示画布的左上角与感兴趣点之间的距离,我们应该将其移动weight*dimension*(delta zoom)
。
请注意,为了计算权重,我们需要考虑维度的放大/缩小值。因此,如果宽度为100
且缩放为0.5
,则我们应假设宽度为50
。所以所有的值都是绝对的(因为鼠标(x,y)
是绝对的)。
所以,它就像:
function worldZoom(e) {
const {x, y, deltaY} = e;
const direction = deltaY > 0 ? -1 : 1;
const factor = 0.01;
const zoom = 1 * direction * factor;
// compute the weights for x and y
const wx = (x-controls.view.x)/(width*controls.view.zoom);
const wy = (y-controls.view.y)/(height*controls.view.zoom);
// apply the change in x,y and zoom.
controls.view.x -= wx*width*zoom;
controls.view.y -= wy*height*zoom;
controls.view.zoom += zoom;
}
您可以在this codepen中尝试。
或者更简单一点,计算内联权重:
controls.view.x -= (x-controls.view.x)/(controls.view.zoom)*zoom;
controls.view.y -= (y-controls.view.y)/(controls.view.zoom)*zoom;
controls.view.zoom += zoom;