我正在测试鼠标是否位于对象上。问题是对象已被转换。我有对象图,主要是相机,然后是滑块对象,最后是形状对象。我需要能够看到鼠标坐标是否在相对于形状对象的指定矩形内。
在这里,我有我的游戏循环,它会转换清除画布,然后转换相机。然后我进入for循环并遍历所有调用其特定“draw”方法的对象,传入已经转换的上下文。
Game.prototype.gameLoop = function()
{
this.context.clearRect(0,0,this.canvas.width, this.canvas.height);
this.context.save();
this.context.translate(this.canvas.width/2, this.canvas.height/2);
this.context.scale(this.camera.scale,this.camera.scale);
this.context.rotate(this.camera.rotate);
this.context.translate(this.camera.x,this.camera.y);
for(var i=0;i<this.objects.length;i++)
{
this.objects[i].update();
this.objects[i].draw(this.context);
}
this.context.restore();
}
这是绘制方法的对象之一。该对象称为Slider。它被成功调用并根据它的x,y和旋转值执行变换。
Slider.prototype.draw = function(ctx)
{
ctx.save();
ctx.translate(this.x,this.y);
ctx.rotate(this.rotate);
this.pointer.draw(ctx);
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(-(this.width/2),0);
ctx.lineTo((this.width/2),0);
ctx.lineTo((this.width/2),5);
ctx.lineTo(-(this.width/2),5);
ctx.fill();
ctx.restore();
}
最后,我成功调用了Shape的draw方法,并再次转换了上下文。
Shape.prototype.draw = function(ctx)
{
ctx.save();
ctx.translate(this.x,this.y);
ctx.rotate(this.rotate);
if(this.isMouseOver)
ctx.fillStyle = this.color;
else
ctx.fillStyle = this.mouseOverFillColor;
ctx.fill(this.shape);
ctx.restore();
}
最后,这里是鼠标移动时调用的方法,名为“mouseEventListener”。我需要能够转换坐标,以便相对于形状看到它们。
Shape.prototype.mouseEventListener = function(evt,type)
{
console.log(evt.clientX+" "+evt.clientY);
}
有什么想法吗?如果需要,我可以创建一个父指针对象,并使形状指向滑块,滑块指向相机以访问每个父级的x,y,旋转值。
我正在寻找相当于Android的mappoints方法,它根据矩阵转换点。在这种情况下,上下文已被多次转换,我需要一种方法来捕获每个对象的状态,然后转换一些点。
我还希望在没有任何其他库的情况下轻松完成所有这些工作。
谢谢。
答案 0 :(得分:5)
在写这个答案时,我注意到标准刚刚改变了。 HTML standard doc 28 Nov 2016
现在有两个对canvas 2D API的新调用
ctx.getTransform()
ctx.resetTransform()
GetTransform返回DOMMatrix
,它与返回SVGMatrix
的现有currentTransform不同。
目前我还不知道采用和使用这两个新的(最受欢迎和最需要的)电话。
现在回到我的常规 belligerency 回答。
在完美的世界中,你只需要使用
<击> var mat = ctx.currentTransform;
击>
var mat = ctx.getTransform();
返回 DOMMatrix,然后你得到逆矩阵。SVGMatrix
<击> var invMat = mat.inverse();
击>
mat.invertSelf();
然后从屏幕坐标转换为世界坐标
我现在猜测DOMMatrix有a,b,c,d,e,f但是我知道它们是否是2D,因为DOMMatrix是一个3D矩阵。
var wX = screenX * invMat.a + screenY * invMat.c + invMat.e;
var wY = screenX * invMat.b + screenY * invMat.d + invMat.f;
就是这样。
但是由于某种原因,浏览器似乎认为没有这个标准对象我们的生活会更好。编写大量缓慢的javascript更好。
ctx.currentTransform 隐藏在前缀(moz)和标志(chrome实验画布API)后面并忽略IE(不知道Edge会做什么??)
要解决您的问题,您需要使用自己的转换替换所有转换,以便跟踪当前转换。有很多JS库可以帮到你。请注意,大多数人都不知道静态内存/对象池,并且可能会在代码中添加过度的性能命中。我现在不会支持任何lib。
矩阵数学模块的示例
var matrixMath =( function(){
var _a,_b,_c,_d,_e,_f;
var xdx,xdy;
var stack = [];
var stackPos = 0;
var API = {
// matrix = create() // creates identity matrix 1,0,0,1,0,0
// matrix = create([1,0,0,1,0,0]); // creates from array
// matrix = create(matrix); // creates from matrix (copy)
// matrix = create(x, y, scale, rotate); // creates from x,y uniform scale and rotation
// matrix = create(x, y, scaleX, scaleY, rotate); // creates from x,y, scaleX,scaleY, rotation
// matrix = create(a,b,c,d,e,f);
create : function(a,b,c,d,e,f){
var mat = {};
if(a === undefined){
mat.a = mat.d = 1;
mat.b = mat.c = mat.e = mat.f = 0;
return mat;
}
if(Array.isArray(a)){
mat.a = a[0];
mat.b = a[1];
mat.c = a[2];
mat.d = a[3];
mat.e = a[4];
mat.f = a[5];
return mat;
}
if(a.a !== undefined){
mat.a = a.a;
mat.b = a.b;
mat.c = a.c;
mat.d = a.d;
mat.e = a.e;
mat.f = a.f;
return mat
}
if(f === undefined){
mat.e = a;
mat.f = b;
if(e === undefined){
mat.d = mat.a = Math.cos(d) * c;
mat.c = -(mat.b = Math.sin(d) * c);
return mat;
}
mat.d = (mat.a = Math.cos(e)) * d;
mat.c = -(mat.b = Math.sin(e)) * d;
mat.a *= c;
mat.b *= c;
return mat;
}
mat.a = a;
mat.b = b;
mat.c = c;
mat.d = d;
mat.e = e;
mat.f = f;
return mat
},
// point = matrixMultPoint(matrix,x,y,returnPoint) // returnPoint is optional.
matrixMultPoint : function(mA,x,y,point){
if(point === undefined){
point = {};
}
point.x = x * mA.a + y * mA.c + mA.e;
point.y = x * mA.b + y * mA.d + mA.f;
return point;
},
// C = A * B
// resultMatrix = matrixMult(matrixA, matrixB, returnMatrix); // returnMatrix is optional
matrixMult : function(mA,mB,mat){ // mat = mA * mB
if(mat === undefined){
mat = {};
}
mat.a = mA.a * mB.a + mA.c * mB.b;
mat.b = mA.b * mB.a + mA.d * mB.b;
mat.c = mA.a * mB.c + mA.c * mB.d;
mat.d = mA.b * mB.c + mA.d * mB.d;
mat.e = mA.a * mB.e + mA.c * mB.f + mA.e;
mat.f = mA.b * mB.e + mA.d * mB.f + mA.f;
return mat;
},
transform : function(mA,a,b,c,d,e,f){ // mat = mA * mB
_a = mA.a * a + mA.c * b;
_b = mA.b * a + mA.d * b;
_c = mA.a * c + mA.c * d;
_d = mA.b * c + mA.d * d;
mA.e = mA.a * e + mA.c * f + mA.e;
mA.f = mA.b * e + mA.d * f + mA.f;
mA.a = _a;
mA.b = _b;
mA.c = _c;
mA.d = _d;
return mA;
},
setTransform : function(mA,a,b,c,d,e,f){ // mat = mA * mB
mA.a = a;
mA.b = b;
mA.c = c;
mA.d = d;
mA.e = e;
mA.f = f;
return mA;
},
resetStack : function(){ // in case you do not match save and restores
// because you have re inited the 2D context
stackPos = 0;
stack.length = 0;
},
save : function(mA){
var mat;
if(stack.length <= stackPos){
stack[stackPos++] = API.create(mA);
return;
}
mat = stack[stackPos++];
mat.a = mA.a;
mat.b = mA.b;
mat.c = mA.c;
mat.d = mA.d;
mat.e = mA.e;
mat.f = mA.f;
},
restore : function(mA){
var mat;
if(stackPos > 0){
stackPos -= 1;
mat = stack[stackPos];
mA.a = mat.a;
mA.b = mat.b;
mA.c = mat.c;
mA.d = mat.d;
mA.e = mat.e;
mA.f = mat.f;
}
return mA;
},
identity : function(mA){
mA.a = mA.d = 1;
mA.b = mA.c = mA.e = mA.f = 0;
return mA;
},
translate : function(mA, x,y){ // mat = mat * translate(x,y)
mA.e = mA.a * x + mA.c * y + mA.e;
mA.f = mA.b * x + mA.d * y + mA.f;
return mA;
},
rotate : function(mA,rotate){
xdx = Math.cos(rotate);
xdy = Math.sin(rotate);
_a = mA.a * xdx + mA.c * xdy;
_b = mA.b * xdx + mA.d * xdy;
_c = mA.c * xdx - mA.a * xdy;
_d = mA.d * xdx - mA.b * xdy;
mA.a = _a;
mA.b = _b;
mA.c = _c;
mA.d = _d;
return mA;
},
// Scales mA
// matrix = scale(mA,scale); // uniform scale
// matrix = scale(mA,scaleX,scaleY); // scaleX,scaleY
scale : function(mA,scaleX,scaleY){
if(scaleY === undefined){
scaleY = scaleX;
}
mA.a *= scaleX;
mA.b *= scaleX;
mA.c *= scaleY;
mA.d *= scaleY;
return mA;
},
// invert(matrix,returnMatrix); // returnMatrix is optional
invert : function(mA,mat){
if(mat === undefined){
mat = {};
}
var d = mA.a * mA.d - mA.b * mA.c;
mat.a = mA.d / d;
mat.b = -mA.b / d;
mat.c = -mA.c / d;
mat.d = mA.a / d;
mat.e = (mA.c * mA.f - mA.d * mA.e) / d;
mat.f = -(mA.a * mA.f - mA.b * mA.e) / d;
return mat;
},
getRotate : function(mA,fromY){
return !fromY ? Math.atan2(mA.b,mA.a) : Math.atan2(mA.d,mA.c) ;
},
getScale : function(mA,fromY){
return !fromY ? Math.sqrt(mA.b * mA.b + mA.a * mA.a) : Math.sqrt(mA.c * mA.c + mA.d * mA.d) ;
},
getOrigin : function(mA,point){
if(point === undefined){
point = {};
}
point.x = mA.e;
point.y = mA.f;
return point;
},
}
return API;
})();
&#13;
使用上面的代码镜像所有转换
// do once only
var cMatrix = matrixMath.create(); // create default identity matrix
var cIMatrix = matrixMath.create(); // create a matrix to hold the inverse
var worldP = {x:0,y:0}; // to hold world coordinates.
// in main loop
matrixMath.identity(cMatrix); // resets to default transform.
matrixMath.translate(cMatrix,x,y); // same as ctx.translate
matrixMath.rotate(cMatrix,angle); // same as ctx.rotate
matrixMath.scale(cMatrix,scaleX,scaleY); // same as ctx.scale.. Has variant see code.
matrixMath.transform(cMatrix,a,b,c,d,e,f); // same as ctx.transform
matrixMath.setTransform(cMatrix,a,b,c,d,e,f); // same as ctx.setTransform
matrixMath.save(cMatrix); // save and restores to matrixMath internal stack
matrixMath.restore(cMatrix); // NOTE very simple stack look at code to understand its function.
// set the context to the transform
ctx.setTransfrom(cMatrix.a, cMatrix.b, cMatrix.c, cMatrix.d, cMatrix.e, cMatrix.f);
// Draw what you need.
To get the world coordinates
cIMatrix = matrixMath.invert(cMatrix,cIMatrix);
// screenX,screenY are the screen position you wish to covert to world.
worldP = matrixMath.matrixMultPoint(cIMatrix,screenX,screenY,worldP);
// worldP.x and worldP.y are now in transformed space.
请注意
matrixMath
对象刚刚针对此答案编写,未经测试。虽然已经通过了基本的解析测试,但它可能包含一些拼写错误。它仅用作矩阵数学的如何示例。