将鼠标坐标转换为HTML5 Canvas转换后的上下文

时间:2016-11-27 23:35:17

标签: javascript html5 canvas

我正在测试鼠标是否位于对象上。问题是对象已被转换。我有对象图,主要是相机,然后是滑块对象,最后是形状对象。我需要能够看到鼠标坐标是否在相对于形状对象的指定矩形内。

在这里,我有我的游戏循环,它会转换清除画布,然后转换相机。然后我进入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方法,它根据矩阵转换点。在这种情况下,上下文已被多次转换,我需要一种方法来捕获每个对象的状态,然后转换一些点。

我还希望在没有任何其他库的情况下轻松完成所有这些工作。

谢谢。

1 个答案:

答案 0 :(得分:5)

HOT OFF THE PRESS

在写这个答案时,我注意到标准刚刚改变了。 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();

返回 SVGMatrix DOMMatrix,然后你得到逆矩阵。

<击> 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。

矩阵数学模块的示例

&#13;
&#13;
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;
&#13;
&#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对象刚刚针对此答案编写,未经测试。虽然已经通过了基本的解析测试,但它可能包含一些拼写错误。它仅用作矩阵数学的如何示例。