当前位置: 首页 > 知识库问答 >
问题:

确定鼠标是否点击了画布上的旋转矩形

穆华彩
2023-03-14

我正在使用以下链接中发布的示例:

https://riptutorial . com/html 5-canvas/example/19666/a-transformation-matrix-to-track-translated-rotated-scaled-shape-s-

我正在尝试使用此示例,并对允许在画布上旋转2个矩形进行了一些修改。有几个问题(它们可能是相关的),但让我从一个问题开始:

鼠标点击不再有效。

我希望有人能帮我指出需要什么补救措施,因为我为此纠结了一天多,显然我对Javascrip中的对象和类并不熟悉。代码在这里:https://jsfiddle.net/jackmstein/ngyfrcms/2/

        <!doctype html>

        <html>

        <head>

        <style>

            body{ background-color:white; }

            #canvas{border:1px solid red; }

        </style>

        <script>

        window.onload=(function(){

         

            var canvas=document.getElementById("canvas");

            var ctx=canvas.getContext("2d");

            var cw=canvas.width;

            var ch=canvas.height;

            function reOffset(){

                var BB=canvas.getBoundingClientRect();

                offsetX=BB.left;

                offsetY=BB.top;        

            }

            var offsetX,offsetY;

            reOffset();

            window.onscroll=function(e){ reOffset(); }

            window.onresize=function(e){ reOffset(); }

         

            // Transformation Matrix "Class"

            

            var TransformationMatrix=( function(){

                // private

                var self;

                var m=[1,0,0,1,0,0];

                var reset=function(){ var m=[1,0,0,1,0,0]; }

                var multiply=function(mat){

                    var m0=m[0]*mat[0]+m[2]*mat[1];

                    var m1=m[1]*mat[0]+m[3]*mat[1];

                    var m2=m[0]*mat[2]+m[2]*mat[3];

                    var m3=m[1]*mat[2]+m[3]*mat[3];

                    var m4=m[0]*mat[4]+m[2]*mat[5]+m[4];

                    var m5=m[1]*mat[4]+m[3]*mat[5]+m[5];

                    m=[m0,m1,m2,m3,m4,m5];

                }

                var screenPoint=function(transformedX,transformedY){

                    // invert

                    var d =1/(m[0]*m[3]-m[1]*m[2]);

                    im=[ m[3]*d, -m[1]*d, -m[2]*d, m[0]*d, d*(m[2]*m[5]-m[3]*m[4]), d*(m[1]*m[4]-m[0]*m[5]) ];

                    // point

                    return({

                        x:transformedX*im[0]+transformedY*im[2]+im[4],

                        y:transformedX*im[1]+transformedY*im[3]+im[5]

                    });

                }

                var transformedPoint=function(screenX,screenY){

                    return({

                        x:screenX*m[0] + screenY*m[2] + m[4],

                        y:screenX*m[1] + screenY*m[3] + m[5]

                    });    

                }

                // public

                function TransformationMatrix(){

                    self=this;

                }

                // shared methods

                TransformationMatrix.prototype.translate=function(x,y){

                    var mat=[ 1, 0, 0, 1, x, y ];

                    multiply(mat);

                };

                TransformationMatrix.prototype.rotate=function(rAngle){

                    var c = Math.cos(rAngle);

                    var s = Math.sin(rAngle);

                    var mat=[ c, s, -s, c, 0, 0 ];    

                    multiply(mat);

                };

                TransformationMatrix.prototype.scale=function(x,y){

                    var mat=[ x, 0, 0, y, 0, 0 ];        

                    multiply(mat);

                };

                TransformationMatrix.prototype.skew=function(radianX,radianY){

                    var mat=[ 1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0 ];

                    multiply(mat);

                };

                TransformationMatrix.prototype.reset=function(){

                    reset();

                }

                TransformationMatrix.prototype.setContextTransform=function(ctx){

                    ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);

                }

                TransformationMatrix.prototype.resetContextTransform=function(ctx){

                    ctx.setTransform(1,0,0,1,0,0);

                }

                TransformationMatrix.prototype.getTransformedPoint=function(screenX,screenY){

                    return(transformedPoint(screenX,screenY));

                }

                TransformationMatrix.prototype.getScreenPoint=function(transformedX,transformedY){

                    return(screenPoint(transformedX,transformedY));

                }

                TransformationMatrix.prototype.getMatrix=function(){

                    var clone=[m[0],m[1],m[2],m[3],m[4],m[5]];

                    return(clone);

                }

                // return public

                return(TransformationMatrix);

            })();

         

            // DEMO starts here

         

            // create a rect and add a transformation matrix

            // to track it's translations, rotations & scalings

            var rect1={x:30,y:30,w:50,h:35,matrix:new TransformationMatrix()};

         

        // draw the untransformed rect in black

        //    ctx.strokeRect(rect.x, rect.y, rect.w, rect.h);

            // Demo: label

        //    ctx.font='11px arial';

        //    ctx.fillText('Untransformed Rect',rect.x,rect.y-10);

         

        // transform the canvas & draw the transformed rect in red

            ctx.translate(100,0);

            ctx.scale(2,2);

            ctx.rotate(Math.PI/12);

            // draw the transformed rect

            ctx.strokeStyle='red';

            ctx.strokeRect(rect1.x, rect1.y, rect1.w, rect1.h);

            ctx.font='6px arial';

            // Demo: label

            ctx.fillText('Rect: Translated, rotated & scaled',rect1.x,rect1.y-6); 

            // reset the context to untransformed state

            ctx.setTransform(1,0,0,1,0,0);

            // record the transformations in the matrix

            var m=rect1.matrix;

            m.translate(100,0);

            m.scale(2,2);

            m.rotate(Math.PI/12);

            // use the rect's saved transformation matrix to reposition, 

            //     resize & redraw the rect

            ctx.strokeStyle='blue';

            drawTransformedRect(rect1);

            // Demo: instructions

            ctx.font='14px arial';

            ctx.fillText('Demo: click inside the blue rect',30,200);

            

            

         

            

               // DEMO starts here

         

            // create a rect and add a transformation matrix

            // to track it's translations, rotations & scalings

            var rect2={x:150,y:30,w:50,h:35,matrix:new TransformationMatrix()};

         

        // draw the untransformed rect in black

        //    ctx.strokeRect(rect.x, rect.y, rect.w, rect.h);

            // Demo: label

        //    ctx.font='11px arial';

        //    ctx.fillText('Untransformed Rect',rect.x,rect.y-10);

         

        // transform the canvas & draw the transformed rect in red

            ctx.translate(100,0);

            ctx.scale(2,2);

            ctx.rotate(Math.PI/12);

            // draw the transformed rect

            ctx.strokeStyle='red';

            ctx.strokeRect(rect2.x, rect2.y, rect2.w, rect2.h);

            ctx.font='6px arial';

            // Demo: label

            ctx.fillText('Rect: Translated, rotated & scaled',rect2.x,rect2.y-6); 

            // reset the context to untransformed state

            ctx.setTransform(1,0,0,1,0,0);

            // record the transformations in the matrix

            var m=rect2.matrix;

            m.translate(100,0);

            m.scale(2,2);

            m.rotate(Math.PI/12);

            // use the rect's saved transformation matrix to reposition, 

            //     resize & redraw the rect

            ctx.strokeStyle='blue';

            drawTransformedRect(rect2);

            // Demo: instructions

          

            

         

           

            // redraw a rect based on it's saved transformation matrix

            function drawTransformedRect(r){

                // set the context transformation matrix using the rect's saved matrix

                m.setContextTransform(ctx);

                // draw the rect (no position or size changes needed!)

                ctx.rect( r.x, r.y, r.w, r.h );

                // reset the context transformation to default (==untransformed);

                m.resetContextTransform(ctx);

            }

         

            // is the point in the transformed rectangle?

            function isPointInTransformedRect(r,transformedX,transformedY){

                var p=r.matrix.getScreenPoint(transformedX,transformedY);

                var x=p.x;

                var y=p.y;

                return(x>r.x && x<r.x+r.w && y>r.y && y<r.y+r.h);

            } 

         

            // listen for mousedown events

            canvas.onmousedown=handleMouseDown;

            function handleMouseDown(e){

                // tell the browser we're handling this event

                e.preventDefault();

                e.stopPropagation();

                // get mouse position

                mouseX=parseInt(e.clientX-offsetX);

                mouseY=parseInt(e.clientY-offsetY);

                // is the mouse inside the transformed rect?

                var rect1={x:30,y:30,w:50,h:35,matrix:new TransformationMatrix()};

                if(isPointInTransformedRect(rect1,mouseX,mouseY)){

                    alert('You clicked in the transformed Rect 1');

                }

               

               var rect2={x:150,y:30,w:50,h:35,matrix:new TransformationMatrix()};

                if(isPointInTransformedRect(rect2,mouseX,mouseY)){

                    alert('You clicked in the transformed Rect 2');

                }

             

         

            }

         

            // Demo: redraw transformed rect without using

            //       context transformation commands

            function drawTransformedRect(r,color){

                var m=r.matrix;

                var tl=m.getTransformedPoint(r.x,r.y);

                var tr=m.getTransformedPoint(r.x+r.w,r.y);

                var br=m.getTransformedPoint(r.x+r.w,r.y+r.h);

                var bl=m.getTransformedPoint(r.x,r.y+r.h);

                ctx.beginPath();

                ctx.moveTo(tl.x,tl.y);

                ctx.lineTo(tr.x,tr.y);

                ctx.lineTo(br.x,br.y);

                ctx.lineTo(bl.x,bl.y);

                ctx.closePath();

                ctx.strokeStyle=color;

                ctx.stroke();

            }

         

        }); // end window.onload

        </script>

        </head>

        <body>

            <canvas id="canvas" width=600 height=250></canvas>

        </body>

        </html>

共有1个答案

薛博艺
2023-03-14

注意此答案使用统一变换。(歪斜不起作用,x轴和y轴必须具有相同的刻度);

为了尽可能保持简单,矩形(框)由其中心< code >位置及其宽度和高度< code >大小定义。

const Point = (x = 0, y = 0) => ({x, y});
const Size = (w = 0, h = 0) => ({w, h});
const Rect = (pos, size) = ({pos, size});

我们可以通过根据需要创建转换来创建旋转(转换)的rect。(比创建DOMMatrix或自定义矩阵更快)

function pathRect(rect, ang, scale) { // ang in radians
    const xax = Math.cos(ang) * scale;
    const xay = Math.sin(ang) * scale;
    ctx.setTransform(xax, xay, -xay, xax, rect.pos.x, rect.pos.y);
    ctx.rect(-rect.size.w * 0.5, -rect.size.h * 0.5, rect.size.w, rect.size.h);
}

为了找到相对于矩形的点我们应用应用于矩形的变换的反(反向)。

relPoint 将转换为框相对单位值。也就是说,如果点位于(旋转矩形的)左上角,则 relPoint 将为 {x:0, y:0},中心为 {x: 0.5, y: 0.5},右下角为 {x: 1, y: 1}。这简化了边界测试(参见演示)

    function pointRelative2Rect(point, relPoint, rect, ang, scale) {
        const xax = Math.cos(-ang) / scale;
        const xay = Math.sin(-ang) / scale;
        const x = point.x - rect.pos.x;
        const y = point.y - rect.pos.y;
        relPoint.x = (xax * x - xay * y) / rect.size.w + 0.5;
        relPoint.y = (xay * x + xax * y) / rect.size.h + 0.5;
    }

因此,如果我们有一个鼠标pos,那么我们可以找到它何时超过矩形使用

    pointRelative2Rect(mouse, mouseRel, rect, rot, scale);
    if (mouseRel.x < 0 || mouseRel.x > 1 || mouseRel.y < 0 || mouseRel.y > 1) {
        // mouse outside rect
    } else {
        // mouse over rect
    }

演示程序创建了一个随机的矩形,可以随着时间的推移旋转和缩放。它使用上面概述的方法来测试鼠标是否在< code>rect中。当鼠标悬停时,该框将变为红色。

const ctx = canvas.getContext("2d");
const w = 256, h = 256;

const Point = (x = 0, y = 0) => ({x, y});
const Size = (w = 0, h = 0) => ({w, h});
const Rect = (pos, size) => ({pos, size});
const randI = (min, max) => Math.random() * (max - min) + min;
const mouse = Point(), mouseRel = Point();
document.addEventListener("mousemove", e => { mouse.x = e.pageX; mouse.y = e.pageY; });
const randRect = () => Rect(
    Point(randI(40, 210), randI(40, 210)),
    Size(randI(10, 30), randI(10, 30))
);
const rect = randRect();

function pathRect(rect, ang, scale) { // ang in radians
    const xax = Math.cos(ang) * scale;
    const xay = Math.sin(ang) * scale;
    ctx.setTransform(xax, xay, -xay, xax, rect.pos.x, rect.pos.y);
    ctx.rect(-rect.size.w * 0.5, -rect.size.h * 0.5, rect.size.w, rect.size.h);
}

function pointRelative2Rect(point, resPoint, rect, ang, scale) {
    const xax = Math.cos(-ang) / scale;
    const xay = Math.sin(-ang) / scale;
    const x = point.x - rect.pos.x;
    const y = point.y - rect.pos.y;
    resPoint.x = (xax * x - xay * y) / rect.size.w + 0.5;
    resPoint.y = (xay * x + xax * y) / rect.size.h + 0.5;
}


requestAnimationFrame(renderLoop);
function renderLoop(time) {
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0, 0, w, h);
    const rot = time / 3000;
    const scale = Math.sin(time / 2777) * 0.5 + 2.1;
    ctx.beginPath();
    pointRelative2Rect(mouse, mouseRel, rect, rot, scale);
    if (mouseRel.x < 0 || mouseRel.x > 1 || mouseRel.y < 0 || mouseRel.y > 1) {
        canvas.style.cursor = "default";
        ctx.strokeStyle = "#000";
    } else {
        canvas.style.cursor = "pointer";
        ctx.strokeStyle = "#F00";
    }
    pathRect(rect, rot, scale);
    ctx.lineWidth = 1 / scale;
    ctx.stroke();
    requestAnimationFrame(renderLoop);
}
canvas { 
    position : absolute; 
    top : 0px; 
    left : 0px;
}
<canvas id="canvas" width="256" height="256"><canvas>
 类似资料:
  • 我的任务是在画布上绘制许多矩形,但所有矩形都有一个旋转角度,它们必须在画布上旋转。我在寻找这个问题的解决方案时遇到的许多建议都指出了绘制一个矩形并旋转画布的方法(canvas.rotate(angle)),但它会旋转所有画布,并且只能使用一个矩形。在画布上绘制许多旋转矩形的最佳方法是什么?我想画矩形(单色,带颜料),但不是位图,因为时间效率和内存。 目前我主要的方法是创建一堆画布,在每个画布上绘制

  • 我正在尝试创建一个程序,该程序应该基于两次鼠标左键单击在画布上绘制一个矩形,并通过一次右键单击清除画布。 创建矩形的方式应该是,第一次鼠标点击模拟矩形的一个角,下一次鼠标点击与第一次鼠标点击相比模拟矩形的对角线角。 我被困在如何存储第一次鼠标点击的坐标,然后把第二次鼠标点击用好,因为每个定义的矩形只基于一组坐标创建,这是矩形的左上角。 现在,我的代码所做的只是绘制固定大小的矩形(50x25),这显

  • 我在AS3工作。 我有一个通用的矩形。这个矩形可以有任何长度、任何宽度和任何旋转。我正在尝试求解矩形四个角的x和y坐标。我知道矩形中心的坐标,我知道它的宽度、高度、最高点和最低点之间的y距离以及最远左侧和最远右侧之间的x距离,以及知道旋转。 我的代码目前看起来像这样(当然,对象是有问题的矩形,请记住,当我应用它时,它可以具有任何维度 - 这只是一种可能性。初始宽度和高度是实际的长度和宽度,而后面引

  • 现在我有一个应用程序,可以让用户单击按钮浏览用作画布背景的图片。我想这样做,如果用户单击画布上的某个地方,则在该点放置一个节点。我假设我需要获取鼠标坐标。有没有一个简单的方法可以调用将节点放置在鼠标单击位置,或者我必须在这个链接中选择路线:WPF-使用鼠标事件在画布上绘图?提前谢谢。 编辑:添加了我尝试制作椭圆的代码。不过它不起作用,而且我不确定如何使用鼠标点击椭圆的坐标。我知道对于一行来说,它只

  • HTML5画布API中,第二种类型,也是最方便的变换,就是旋转。本节,我们首先通过平移操作来定位画布上下文,再调用rotate()方法来旋转画布上下文。 图4-3 旋转画布上下文 绘制步骤 按照以下步骤,绘制一个被旋转的矩形: 1. 定义定义画布上下文,及矩形的尺寸: window.onload = function(){ var canvas  = document.getElementB

  • 在HTML和JavaScript中,这应该是一个非常简单的任务。我有一个图标栏,目前有一个单一的可点击链接,新的框。在图标栏下面,我有一张画布。我的目标是在用户每次按下new Box按钮时,在画布上绘制一个新的矩形。 现在,我的图标栏和画布正确显示,我可以单击图标栏的“新建框”链接,但画布上没有出现矩形,尽管我已将其参数指定为:。 null null