【问题标题】:HTML5 canvas: find out if click co-ordinates are inside a given rectangleHTML5 画布:确定点击坐标是否在给定的矩形内
【发布时间】:2016-12-21 01:42:47
【问题描述】:

可能有人在这个困境上有过类似的经历,可以在这里帮助我......

基本上,我有一个画布元素,我使用

在其上循环绘制多个矩形
context.fillRect (x, y, width, height)

现在,我希望一些矩形成为热点并响应点击事件。我可以使用 event.layerXevent.layerY 找出点击事件的确切 (x,y)。

鉴于我知道以下内容:

  • 点击的确切 x,y
  • 每个矩形的 x,y,width 和 height

如何确定点击事件是否发生在某个矩形的周边内
以及,
点击事件发生在哪个矩形上?

有这样的数学公式吗?

任何帮助将不胜感激,如果我不够清楚,请告诉我...

谢谢

编辑
没有比遍历所有矩形并检查它们的位置和尺寸更好的方法吗?

【问题讨论】:

    标签: javascript events canvas


    【解决方案1】:

    这是使用地图时经常遇到的一般拓扑问题。

    判断一个点是否在任何多边形(矩形、圆形、不规则形状)内的算法如下:

    • 从被检查点开始沿任意方向构建任意线,直到多边形所在的屏幕区域边缘

    • 如果该线在 odd 个位置与任何多边形边界相交,则它在 在该多边形内

    • 如果该线与任何多边形边界相交,则 偶数 个位置位于该多边形之外

             ------------
             |           |
             |        o--|-------------------    1 intersection: inside
             |           |
             -------------
             ------------
             |           |
      o------|-----------|-------------------    2 intersections: outside
             |           |
             -------------
      

    注意事项:

    • 线的方向无关

    • 如果在屏幕一侧切割多边形而不关闭,将不起作用

    • 如果多边形自切,那么如果直线 恰好通过了切点(例如在图 8 中被视为 直线恰好穿过上下位置的单个多边形 部分连接图连接)

    【讨论】:

      【解决方案2】:

      我不想在这里发布大量代码,但这里有一个 js 类可能会对您有所帮助...

      function Point(x,y){
          this.x=x;
          this.y=y;
      }
      
      function Polygon(){
          this.points=[];
          this.x_min=undefined;
          this.x_max=undefined;
          this.y_min=undefined;
          this.y_max=undefined;
      
          this.add = function(p){
              this.points=this.points.concat(p);
              if (p.x<this.x_min){this.x_min=p.x;}
              if (p.x>this.x_max){this.x_max=p.x;}
              if (p.y<this.y_min){this.y_min=p.y;}
              if (p.y>this.y_min){this.y_max=p.y;}
          }
      
          this.pointInPoly = function(p){
              var j=(this.points.length-1);  //start by testing the link from the last point to the first point
              var isOdd=false;
      
              //check the bounding box conditions
              if (p.x < this.x_min || p.x > this.x_max || p.y < this.y_min || p.y > this.y_max){
                  return false;
              }
      
              //if necessary use the line crossing algorithm
              for(var i=0; i<this.points.length; i++){
                  if ((this.points[i].y<p.y && this.points[j].y>=p.y) ||  
                      (this.points[j].y<p.y && this.points[i].y>=p.y)) {
                          if (this.points[i].x+(p.y-this.points[i].y)/(this.points[j].y-
                              this.points[i].y)*(this.points[j].x-this.points[i].x)<p.x)
                          { isOdd=(!isOdd);} }
                  j=i;
              }
              return isOdd;
          }
      }
      

      PS:您需要使用类似以下函数(其中 e 是您的点击事件)将点击事件转换为本地坐标:

      function getPosition(e){
          var p = new Point();
          if (e.pageX != undefined && e.pageY != undefined) {
              p.x = e.pageX;
              p.y = e.pageY;
           }
           else {
              p.x = e.clientX + document.body.scrollLeft +
                      document.documentElement.scrollLeft;
              p.y = e.clientY + document.body.scrollTop +
                      document.documentElement.scrollTop;
          }
          p.x -= gCanvas.offsetLeft;
          p.y -= gCanvas.offsetTop;
      
      
          return p;
      }
      

      【讨论】:

        【解决方案3】:

        大致上,你可以这样做:

        var click_x = event.layerX;
        var click_y = event.layerY;
        
        for ( var i = 0; i < rects.length; i++ ) {
            var rect = rects[i];
            if ( click_x >= rect.x && click_x <= rect.x + rect.width
            &&   click_y >= rect.y && click_y <= rect.y + rect.height ) {
                // The click was inside the rectange
            }
        }
        

        假设我们正在处理非负宽度和高度:)

        【讨论】:

        • 除了遍历每个矩形并检查尺寸之外,没有更好的方法...(叹息!!)
        • 您可以使用bounding volume hierarchy (BVH) 之类的东西来分割空间并缩小需要检查的矩形。不过,这需要一些预处理。
        【解决方案4】:

        这可能有点矫枉过正,但 Cake JS 可以处理矩形和其他形状。 Check out the demo.

        【讨论】:

        • 他有一个完整的事件处理子系统,你是对的,这对我的需求来说有点矫枉过正。
        【解决方案5】:

        我最近创建了一个用于检测矩形上的鼠标点击的 API。这是非常基本的,但它应该可以解决您的问题
        你可以找到它here
        注意:它目前不支持按钮的文本,但支持一些基本样式
        文档: Here 代码:

        function canvasButton(x, y, w, h, style , text) {
          this.style = style
          this.x = x || 0;
          this.y = y || 0;
          this.w = w || 1;
          this.h = h || 1;
          this.fill = style.default.fill ;
          this.text = text || undefined;
          this.onclick = function() {alert("Click! Make sure to add the onclick listener to your button!")}
          this.mouseup = function() {alert("Mouseup! Make sure to add the mouseup listener to your button!")}
          this.addListener = function() {
            var buttonX = this.x;
            var buttonY = this.y;
            var buttonWidth = this.w;
            var buttonHeight = this.h;
            var onclick = this.onclick
            var mouseup = this.mouseup
            var styles = this.style
            var draw = this.draw
            var ctx = this.ctx
            this.ctx.canvas.addEventListener('mousedown',function(e){
                var click_x = e.clientX;
                var click_y = e.clientY;
                if ( click_x >= buttonY && click_x <= buttonX + buttonWidth && click_y >= buttonY && click_y <= buttonY + buttonHeight ) {  
                    onclick()
                    ctx.fillStyle = style.active.fill
                    ctx.fillRect(buttonX, buttonY, buttonWidth, buttonHeight);
                }
            })
            this.ctx.canvas.addEventListener('mouseup',function(e) {
                var click_x = e.clientX;
                var click_y = e.clientY;
                if ( click_x >= buttonY && click_x <= buttonX + buttonWidth && click_y >= buttonY && click_y <= buttonY + buttonHeight ) {  
                    mouseup()
                    ctx.fillStyle = style.default.fill
                    ctx.fillRect(buttonX, buttonY, buttonWidth, buttonHeight);
        
                }
        
            })
          }
        
        }
        
        // Draws this shape to a given context
        canvasButton.prototype.draw = function(ctx) {
          this.ctx = ctx
          ctx.fillStyle = this.fill;
          ctx.font = this.font
          ctx.fillRect(this.x, this.y, this.w, this.h);
          ctx.fillText(this.text,this.x,this.y)
          this.addListener();
        }
        
        
        //Use it like this:
        var start = new canvasButton(x,y,height,width,{ // Styles
            "default": {
                "fill":"#765",
        
            }, 
            "active": {
                "fill":"#876",
        
            } 
        })
        //Event Listeners
        start.mousedown = function() {
        
        }
        start.mouseup = function() {
        
        }
        start.draw(document.getElementById("canvas").getContext("2d")); // Draw to canvas
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-17
          • 1970-01-01
          • 2020-09-07
          • 1970-01-01
          • 2020-07-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多