回答问题
您没有提供一些代码。具体来说,您正在创建每个事件的向量对象可能会在那里。(实际上,你不应该每次都创建一个新对象。只需创建一次并更新值即可)
我看到的是mouseMove事件不会更新之前的鼠标位置对象,因此您将只从上次鼠标按下开始移动。但是您可能希望如此。因此,没有代码,我不知道哪里有问题,因为给定的代码是可以的。
以下是我整个shabang的方法..
如何平移(和缩放)。
以下是使用鼠标进行平移和缩放的示例。它比标准平移和缩放要复杂一些,因为我添加了一些平滑处理以使其具有更好的交互感。
它的工作原理。
画布使用变换矩阵来转换点。这样做的效果是维护该矩阵。我称变换空间为实际空间。我还保持一个反转矩阵,用于将屏幕空间转换为实际空间。
演示的核心在于对象 displayTransform
,它保存矩阵,所有需要的单个值以及函数 update()
(每帧调用), setHome()
获取屏幕空间变换并将其应用于画布。用于清除屏幕。和 setTransform()
这将画布设置为实际空间(缩放平移空间)
为了平滑运动,我有一个值的镜像 x,y,ox,oy,scale,rotate
。(ox,oy是原点x和y)(是的,旋转可以工作)每个这些变量都有一个delta前缀和一个chaser前缀。追逐者值追逐所需的值。您不应该触摸追逐者值。有两个值称为 drag
和 accel
(加速度的简称) drag
(不是真正模拟的drag)是delta衰减的速度。 drag
> 0.5的值会产生弹性响应。随着你接近一个,它会变得越来越有弹性。在1处,绑定将不会停止,在1以上,它是无法使用的。 "accel"是转换对鼠标移动的响应速度。低值为慢响应,0为完全无响应,而1为即时响应。尝试使用这些值找到您喜欢的值。
追逐者值逻辑的示例
var x = 100;
var dx = 0;
var cx = 0;
var drag = 0.1;
var accel = 0.9;
dx += (x-cx)*accel;
dx *= drag;
cx += dx;
转换坐标
如果您不知道物体在哪里,就没有必要拥有一个缩放、平移和旋转的画布。为此,我保留了一个反矩阵来将屏幕上的x和y坐标转换为实际空间中的x和y坐标。为方便起见,我每次更新都将鼠标转换为实际空间。如果您想将实际空间转换为屏幕空间,则只需执行以下操作:
var x;
var y;
x -= this.cx;
y -= this.cy;
screenX = (x * this.matrix[0] + y * this.matrix[2])+this.cox;
screenY = (x * this.matrix[1] + y * this.matrix[3])+this.coy;
你可以在鼠标的末尾看到它
displayTransform.update
,我在那里使用逆变换将鼠标屏幕坐标转换为实际坐标。然后在主更新循环中,我使用鼠标实际坐标来显示帮助文本。我让代码的用户创建一个函数来转换任何屏幕坐标(很容易,只需挤压将鼠标转换的部分)。
缩放
使用鼠标滚轮进行缩放。这会产生一些问题,并且通常您希望缩放以鼠标为中心。但是,变换实际上是相对于屏幕左上角的。为了解决这个问题,我还保留了一个原点x和y。这基本上漂浮着,直到需要滚轮缩放,然后设置为鼠标的实际位置,并将鼠标距离左上角的距离放置在变换x和y位置。然后只需增加或减小比例即可缩放。我将原点和偏移浮动(未设置追逐值)。这适用于当前的拖动和加速设置,但如果您注意到其他设置不起作用,则设置cx,cy,cox,coy值。(我已经在代码中添加了一个注释)
平移
使用左鼠标按钮进行平移。单击并拖动以平移。这很简单。我获取上次鼠标位置和新位置屏幕空间(由鼠标事件给出的坐标)之间的差异。这给我一个鼠标增量向量。我将增量鼠标向量转换为实际空间,并从左上角坐标
displayTransform.x
和
displayTransform.y
中减去该向量。就这样,我让chaser x和y平滑处理它所有。
代码片段只显示可以平移和缩放的大图像。我检查完整标志而不是使用onload。在图像加载时,片段将仅显示加载。主循环使用requestAnimationFrame刷新,首先更新
displayTransform
然后在home空间(屏幕空间)中清除画布,然后在实际空间中显示图像。像往常一样,我会与时间战斗,因此将根据时间允许返回添加更多注释,也许还有一两个函数。
如果您发现chase变量有些太多,请只需删除它们,并将所有带前缀的c变量替换为未带前缀的变量。
好了,希望对您有所帮助。还没有完成,需要清理,但需要做一些真正的工作一段时间。
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var mouse = {
x : 0,
y : 0,
w : 0,
alt : false,
shift : false,
ctrl : false,
buttonLastRaw : 0,
buttonRaw : 0,
over : false,
buttons : [1, 2, 4, 6, 5, 3],
};
function mouseMove(event) {
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if (mouse.x === undefined) {
mouse.x = event.clientX;
mouse.y = event.clientY;
}
mouse.alt = event.altKey;
mouse.shift = event.shiftKey;
mouse.ctrl = event.ctrlKey;
if (event.type === "mousedown") {
event.preventDefault()
mouse.buttonRaw |= mouse.buttons[event.which-1];
} else if (event.type === "mouseup") {
mouse.buttonRaw &= mouse.buttons[event.which + 2];
} else if (event.type === "mouseout") {
mouse.buttonRaw = 0;
mouse.over = false;
} else if (event.type === "mouseover") {
mouse.over = true;
} else if (event.type === "mousewheel") {
event.preventDefault()
mouse.w = event.wheelDelta;
} else if (event.type === "DOMMouseScroll") {
mouse.w = -event.detail;
}
}
function setupMouse(e) {
e.addEventListener('mousemove', mouseMove);
e.addEventListener('mousedown', mouseMove);
e.addEventListener('mouseup', mouseMove);
e.addEventListener('mouseout', mouseMove);
e.addEventListener('mouseover', mouseMove);
e.addEventListener('mousewheel', mouseMove);
e.addEventListener('DOMMouseScroll', mouseMove);
e.addEventListener("contextmenu", function (e) {
e.preventDefault();
}, false);
}
setupMouse(canvas);
var displayTransform = {
x:0,
y:0,
ox:0,
oy:0,
scale:1,
rotate:0,
cx:0,
cy:0,
cox:0,
coy:0,
cscale:1,
crotate:0,
dx:0,
dy:0,
dox:0,
doy:0,
dscale:1,
drotate:0,
drag:0.1,
accel:0.7,
matrix:[0,0,0,0,0,0],
invMatrix:[0,0,0,0,0,0],
mouseX:0,
mouseY:0,
ctx:ctx,
setTransform:function(){
var m = this.matrix;
var i = 0;
this.ctx.setTransform(m[i++],m[i++],m[i++],m[i++],m[i++],m[i++]);
},
setHome:function(){
this.ctx.setTransform(1,0,0,1,0,0);
},
update:function(){
this.dx += (this.x-this.cx)*this.accel;
this.dy += (this.y-this.cy)*this.accel;
this.dox += (this.ox-this.cox)*this.accel;
this.doy += (this.oy-this.coy)*this.accel;
this.dscale += (this.scale-this.cscale)*this.accel;
this.drotate += (this.rotate-this.crotate)*this.accel;
this.dx *= this.drag;
this.dy *= this.drag;
this.dox *= this.drag;
this.doy *= this.drag;
this.dscale *= this.drag;
this.drotate *= this.drag;
this.cx += this.dx;
this.cy += this.dy;
this.cox += this.dox;
this.coy += this.doy;
this.cscale += this.dscale;
this.crotate += this.drotate;
this.matrix[0] = Math.cos(this.crotate)*this.cscale;
this.matrix[1] = Math.sin(this.crotate)*this.cscale;
this.matrix[2] = - this.matrix[1];
this.matrix[3] = this.matrix[0];
this.matrix[4] = -(this.cx * this.matrix[0] + this.cy * this.matrix[2])+this.cox;
this.matrix[5] = -(this.cx * this.matrix[1] + this.cy * this.matrix[3])+this.coy;
var det = (this.matrix[0] * this.matrix[3] - this.matrix[1] * this.matrix[2]);
this.invMatrix[0] = this.matrix[3] / det;
this.invMatrix[1] = - this.matrix[1] / det;
this.invMatrix[2] = - this.matrix[2] / det;
this.invMatrix[3] = this.matrix[0] / det;
if(mouse !== undefined){
if(mouse.oldX !== undefined && (mouse.buttonRaw & 1)===1){
var mdx = mouse.x-mouse.oldX;
var mdy = mouse.y-mouse.oldY;
var mrx = (mdx * this.invMatrix[0] + mdy * this.invMatrix[2]);
var mry = (mdx * this.invMatrix[1] + mdy * this.invMatrix[3]);
this.x -= mrx;
this.y -= mry;
}
if(mouse.w !== undefined && mouse.w !== 0){
this.ox = mouse.x;
this.oy = mouse.y;
this.x = this.mouseX;
this.y = this.mouseY;
if(mouse.w > 0){
this.scale *= 1.1;
mouse.w -= 20;
if(mouse.w < 0){
mouse.w = 0;
}
}
if(mouse.w < 0){
this.scale *= 1/1.1;
mouse.w += 20;
if(mouse.w > 0){
mouse.w = 0;
}
}
}
var screenX = (mouse.x - this.cox);
var screenY = (mouse.y - this.coy);
this.mouseX = this.cx + (screenX * this.invMatrix[0] + screenY * this.invMatrix[2]);
this.mouseY = this.cy + (screenX * this.invMatrix[1] + screenY * this.invMatrix[3]);
mouse.rx = this.mouseX;
mouse.ry = this.mouseY;
mouse.oldX = mouse.x;
mouse.oldY = mouse.y;
}
}
}
var img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/e/e5/Fiat_500_in_Emilia-Romagna.jpg"
ctx.font = "14px verdana";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var timer =0;
function update(){
timer += 1;
displayTransform.update();
displayTransform.setHome();
ctx.clearRect(0,0,canvas.width,canvas.height);
if(img.complete){
displayTransform.setTransform();
ctx.drawImage(img,0,0);
ctx.fillStyle = "white";
if(Math.floor(timer/100)%2 === 0){
ctx.fillText("Left but to pan",mouse.rx,mouse.ry);
}else{
ctx.fillText("Wheel to zoom",mouse.rx,mouse.ry);
}
}else{
displayTransform.setTransform();
ctx.fillText("Loading image...",100,100);
}
if(mouse.buttonRaw === 4){
displayTransform.x = 0;
displayTransform.y = 0;
displayTransform.scale = 1;
displayTransform.rotate = 0;
displayTransform.ox = 0;
displayTransform.oy = 0;
}
requestAnimationFrame(update);
}
update();
.canC { width:400px; height:400px;}
div {
font-size:x-small;
}
<div>Wait for image to load and use <b>left click</b> drag to pan, and <b>mouse wheel</b> to zoom in and out. <b>Right click</b> to return to home scale and pan. Image is 4000 by 2000 plus so give it time if you have a slow conection. Not the tha help text follows the mouse in real space. Image from wiki commons</div>
<canvas class="canC" id="canV" width=400 height=400></canvas>