【问题标题】:React Canvas, detect region in image at mouse click positionReact Canvas,在鼠标单击位置检测图像中的区域
【发布时间】:2021-10-06 13:09:35
【问题描述】:

我有一张带有这张图片的画布

我希望当我点击画布时,我会在我点击的位置上显示区域

例如,当我点击红点时,我会得到区域 1-2-3-4-5-6

// input: mousePosition: {x, y}
// output: listPoint: [
//  {x1, y1},
//  {x2, y2},
//  ...
//  {x6, y6}
// ]

有没有人有任何想法或任何工具可以帮助我。谢谢

【问题讨论】:

  • 将画布渲染为图像并使用图像映射?
  • 你能在画布上创建一个原生的Javascript点击事件来检测点击位置吗?您知道应该处理您的点击的区域的坐标吗?
  • 我想要一些诸如图像处理之类的东西来检测该区域。如上例,我的输入已经是鼠标位置,我希望输出是区域的列表点
  • 您将如何直观地识别点击应该表现特殊的区域?
  • 这张图片是地图还是地理空间数据?如果是,您可以使用 GIS 和 JavaScript 映射库将处理鼠标单击和多边形图层的交集,然后您可以获得单击的形状的周长。

标签: javascript reactjs image-processing canvas


【解决方案1】:

如果没有图像中的点列表似乎是不可能的,我找到了另一个解决方案:(

【讨论】:

    【解决方案2】:

    如果您有点和形状的列表,则可以使用click 事件找出您所在的位置。

    来自this math.stackexchange.com 上的答案的“替代方法”

    为了确定我们是否处于某个形状中,我们从鼠标点击的地方到画布外部画一条线段。然后我们计算它与形状边缘相交的次数。如果交叉点的数量是奇数,那么我们就在形状中。然后我们对每个形状执行此操作,直到找到鼠标所在的位置。

    要计算两条线是否相交,我们可以使用thisGeeksforGeeks 文章中的数学。它通过比较线中点的方向来工作。

    下面有一个例子。

    let globalPoints = [
        {"x":155,"y":395,"n":"A"}/*0*/,
        {"x":235,"y":630,"n":"B"}/*1*/,
        {"x":0,"y":630,"n":"C"}/*2*/,
        {"x":0,"y":380,"n":"D"}/*3*/,
        {"x":0,"y":80,"n":"E"}/*4*/,
        {"x":405,"y":80,"n":"F"}/*5*/,
        {"x":385,"y":355,"n":"G"}/*6*/,
        {"x":224,"y":598,"n":"H"}/*7*/,
        {"x":755,"y":520,"n":"I"}/*8*/,
        {"x":795,"y":80,"n":"J"}/*9*/,
        {"x":890,"y":85,"n":"K"}/*10*/,
        {"x":890,"y":630,"n":"L"}/*11*/,
        {"x":855,"y":630,"n":"M"}/*12*/,
        {"x":0,"y":0,"n":"N"}/*13*/,
        {"x":890,"y":0,"n":"O"}/*14*/
    ];
    
    let shapePoints = [
        [0,1,2,3],
        [3,4,5,6,0],
        [0,7,8,9,5,6],
        [9,10,11,12,8],
        [9,10,11,12,8],
        [7,1,12,8],
        [4,13,14,10,9,5]
    ];
    
    let lastClick = null;
    
    class Shape {
    
        constructor(points)
        {
            this.points = points;
            this.filledIn = false;
        }
    
        draw(ctx) {
    
            ctx.beginPath();
            ctx.strokeStyle = 'black';
            ctx.lineWidth = 3;
    
            ctx.fillStyle = 'red';
    
            ctx.moveTo(globalPoints[this.points[0]].x,globalPoints[this.points[0]].y);
    
            for (let i =1;i < this.points.length;i++) {
                ctx.lineTo(globalPoints[this.points[i]].x,globalPoints[this.points[i]].y);
            }
    
            ctx.closePath();
    
            if (this.filledIn) {
                ctx.fill();
            }
    
            ctx.stroke();
        }
    
        toString()
        {
            let name = "";
            for (let i=0;i<this.points.length;i++) {
                name += globalPoints[this.points[i]].n;
            }
    
    
            return name;
        }
    
        //use the 'alternate method' from this answer
        //https://math.stackexchange.com/questions/926559/when-is-a-point-in-the-plane-inside-a-simple-closed-path
        calculateMouseClick(mouseX,mouseY)
        {
            //generate a ray that extends to outside of the shape,
            //we know -1,-1 will be outside as all points are positive
            let ray = {"p1":{"x":mouseX,"y":mouseY},"p2":{"x":-1,"y":-1}};
    
            let numIntersections = 0;
    
            //calculate all but last lines
            for (let i=0;i<this.points.length-1;i++) {
                let shapeEdge = {
                    "p1":{
                        "x":globalPoints[this.points[i]].x,
                        "y":globalPoints[this.points[i]].y
                    },
                    "p2":{
                        "x":globalPoints[this.points[i+1]].x,
                        "y":globalPoints[this.points[i+1]].y
                    }
                };
    
                if (doIntersect(ray.p1,ray.p2,shapeEdge.p1,shapeEdge.p2)) {
                    numIntersections++;
                }
            }
    
            //last line wraps around to start of array
            let shapeEdge = {
                "p1":{
                    "x":globalPoints[this.points[0]].x,
                    "y":globalPoints[this.points[0]].y
                },
                "p2":{
                    "x":globalPoints[this.points[this.points.length-1]].x,
                    "y":globalPoints[this.points[this.points.length-1]].y
                }
            };
    
            if (doIntersect(ray.p1,ray.p2,shapeEdge.p1,shapeEdge.p2)) {
                numIntersections++;
            }
    
            if (numIntersections == 0 || numIntersections%2 == 0) {
                return false;
            }
    
            return true;
        }
    
    }
    
    let shapes = [];
    
    function init()
    {
        "use strict";
        for (let i = 0;i< globalPoints.length;i++) {
            //add 50 to x and y so we don't draw at the very edge of the canvas
            globalPoints[i].x += 50;
            globalPoints[i].y += 80;
        }
    
        for (let i = 0;i < shapePoints.length;i++) {
            shapes.push(new Shape(shapePoints[i]));
        }
    
        draw();
    
        let canvas = document.getElementById("drawingArea");
        canvas.addEventListener("click",canvasClicked);
    }
    
    function draw()
    {
        let canvas = document.getElementById("drawingArea");
        let ctx = canvas.getContext('2d');
    
        ctx.clearRect(0,0,canvas.width,canvas.height);
        drawShapes(ctx);
        drawPoints(ctx);
        drawClick(ctx);
    }
    
    function drawShapes(ctx)
    {
        "use strict";
    
        for (let i = 0;i<shapes.length;i++) {
            shapes[i].draw(ctx);
        }
    }
    
    function drawPoints(ctx)
    {
        "use strict";
    
        ctx.font = "48px sans";
        for (let i = 0;i < globalPoints.length;i++) {
            ctx.fillStyle = "black";
            ctx.beginPath();
            ctx.arc(globalPoints[i].x,globalPoints[i].y,5,0,2*Math.PI);
            ctx.fill();
            ctx.fillStyle = "#444444";
            ctx.fillText(globalPoints[i].n,globalPoints[i].x-40,globalPoints[i].y-25);
        }
    }
    
    function drawClick(ctx)
    {
        "use strict";
        if (lastClick == null) {
            return;
        }
    
        ctx.strokeStyle = "#33dd33";
        ctx.arc(lastClick.x,lastClick.y,10,0,2*Math.PI);
        ctx.fill();
    }
    
    function canvasClicked(event)
    {
        "use strict";
    
        let mouseX = event.offsetX;
        let mouseY = event.offsetY;
        lastClick = {"x":mouseX,"y":mouseY};
    
        //clear out previous click
        for (let i=0;i<shapes.length;i++) {
            shapes[i].filledIn = false;
        }
    
        let foundShape = false;
        let shapeNameElem = document.getElementById("shapeName");
        for (let i=0;i<shapes.length;i++) {
            let isIn = shapes[i].calculateMouseClick(mouseX,mouseY);
    
            if (isIn) {
                shapes[i].filledIn = true;
                foundShape = true;
                shapeNameElem.innerHTML = "Clicked shape: "+shapes[i].toString();
                break;
            }
        }
    
        if (!foundShape) {
            shapeNameElem.innerHTML="";
        }
    
    
        draw();
    }
    
    /********************************
    onSegment(), orientation(), doIntersect()
    https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
    
    license CC BY-SA
    https://www.geeksforgeeks.org/copyright-information/
    ********************************/
    
    // Given three colinear points p, q, r, the function checks if
    // point q lies on line segment 'pr'
    function onSegment(p, q, r)
    {
        if (q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) &&
            q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y))
        return true;
    
        return false;
    }
     
    // To find orientation of ordered triplet (p, q, r).
    // The function returns following values
    // 0 --> p, q and r are colinear
    // 1 --> Clockwise
    // 2 --> Counterclockwise
    function orientation(p, q, r)
    {
     
        // See https://www.geeksforgeeks.org/orientation-3-ordered-points/
        // for details of below formula.
        let val = (q.y - p.y) * (r.x - q.x) -
                (q.x - p.x) * (r.y - q.y);
    
        if (val == 0) return 0; // colinear
    
        return (val > 0)? 1: 2; // clock or counterclock wise
    }
    
    // The main function that returns true if line segment 'p1q1'
    // and 'p2q2' intersect.
    function doIntersect(p1, q1, p2, q2)
    {
     
        // Find the four orientations needed for general and
        // special cases
        let o1 = orientation(p1, q1, p2);
        let o2 = orientation(p1, q1, q2);
        let o3 = orientation(p2, q2, p1);
        let o4 = orientation(p2, q2, q1);
    
        // General case
        if (o1 != o2 && o3 != o4)
            return true;
    
        // Special Cases
        // p1, q1 and p2 are colinear and p2 lies on segment p1q1
        if (o1 == 0 && onSegment(p1, p2, q1)) return true;
    
        // p1, q1 and q2 are colinear and q2 lies on segment p1q1
        if (o2 == 0 && onSegment(p1, q2, q1)) return true;
    
        // p2, q2 and p1 are colinear and p1 lies on segment p2q2
        if (o3 == 0 && onSegment(p2, p1, q2)) return true;
    
        // p2, q2 and q1 are colinear and q1 lies on segment p2q2
        if (o4 == 0 && onSegment(p2, q1, q2)) return true;
    
        return false; // Doesn't fall in any of the above cases
    } 
    
    
    
    window.addEventListener("load",init);
    <html>
        <head>
            <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
            <script src="test.js"></script>
            <link rel="stylesheet" href="test.css"/>
        </head>
        <body>
      <p id="shapeName"></p>
            <canvas id="drawingArea" width="1000" height="800"></canvas>
            
        </body>
    </html>

    【讨论】:

    • 谢谢,这不是我要找的
    【解决方案3】:

    这可以通过 HTML map areas 实现。我冒昧地在 500 像素 x 450 像素的区域上绘制了您图像中的坐标。

    创建不同区域后,您可以使每个区域包含href 属性,该属性可以转到该区域的页面或使用以下格式的 URL 执行 JavaScript 代码:javascript:insert_your_code_here

    如果您想获取坐标x, y 上的哪个区域,您可以在所述坐标上模拟点击事件。

    结合所有这些,这是我想出的代码:

    document.querySelector("#submitCoords").addEventListener("click", () => {
      const x = document.querySelector("#xInput").value;
      const y = document.querySelector("#yInput").value;
    
      document.elementFromPoint(x, y).click();
    });
    
    const clicked = (area) => {
      document.querySelector(
        "#clickResults"
      ).innerText = `Clicked ${area} area.`;
    };
    <img src="https://i.imgur.com/myv6wss.jpeg" id="image" alt="Image" usemap="#workmap" width="500" height="450" />
    
    <map name="workmap" id="workmap">
          <area
            shape="poly"
            coords="46,46,223,53,213,183,101,203,46,199"
            alt="A area"
            href="javascript:clicked('A')"
          />
          <area
            shape="poly"
            coords="409,57,446,57,446,348,390,262"
            alt="B area"
            href="javascript:clicked('B')"
          />
          <area
            shape="poly"
            coords="46,199,101,203,134,298,148,340,46,340"
            alt="C area"
            href="javascript:clicked('C')"
          />
          <area
            shape="poly"
            coords="148,340,134,298,390,262,446,348"
            alt="D area"
            href="javascript:clicked('D')"
          />
          <area
            shape="poly"
            coords="223,53,409,57,390,262,134,298,101,203,213,183"
            alt="Center area"
            href="javascript:clicked('Center')"
          />
        </map>
    
    <br />
    <input type="number" placeholder="Input x coordinate here" id="xInput" />
    <input type="number" placeholder="Input y coordinate here" id="yInput" />
    <br />
    <br />
    <button id="submitCoords">Submit coordinates</button>
    
    <p id="clickResults"></p>

    Codepen:codepen.io/pen/wvdEYeZ

    【讨论】:

    • 谢谢,这不是我想要的。顺便说一句,仍然给我的赏金第一个答案:)
    猜你喜欢
    • 2018-11-06
    • 1970-01-01
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-13
    • 2018-07-25
    相关资源
    最近更新 更多