如何获取canvas中某一个点的坐标以及位置

image.png
image.png

root是个画布, <canvas id="root"></canvas>

这个画布中,有很多子节点,就是那个框框,然后每个子节点中又有自己的子节点,比如第二个框。

数据类型大概是item[{x,y,w,h,child[{}]}],这样,有x轴,y轴,宽高,子节点

现在我随便点一个点,求这个点在哪个位置,

比如说,这个点在root上,比如说,这个点在某个children上,求位置,求坐标

最终求的就是,我随手点一个点,然后找出这个点的坐标,还有这个点所在的位置,是在root上,还是在某个子节点上

比如第一张图,我随手点了一个点,很明了,她在root上,那么为什么他在root上,写出代码

再比如,这个红点,求她的位置和坐标

感觉跟递归和广度优化,深度优化有关吧,代码应该怎么写?

阅读 12.9k
2 个回答

简单写了一个demo,思路大概就这样。
在线体验:http://jsrun.pro/rwWKp/edit

<div class="container">
  <canvas></canvas>
  <div class="show-click"></div>
</div>
canvas {
  border: 1px solid red;
  height: 600px;
  width: 600px;
}
const WIDTH = 600
const HEIGHT = 600

const NODE_LIST = [
  {
    x: 10,
    y: 20,
    width: 30,
    height: 30,
    text: 'rect1'
  },
  {
      x: 50,
      y: 100,
      height: 300,
    width: 300,
      text: 'rect2',
      children: [
      {
          x: 100,
        y: 150,
        height: 200,
        width: 200,
        text: 'rect2-1',
              children: [
                  {
                  x: 140,
            y: 200,
            height: 50,
            width: 40,
            text: 'rect2-1-1',
                  },
                  {
                  x: 220,
            y: 200,
            height: 50,
            width: 40,
            text: 'rect2-1-2',
                  }
                ]
          }
    ]
  }
]

function _drawRect (nodeList, ctx) {
  nodeList.forEach(node => {
    ctx.strokeRect(node.x, node.y, node.width, node.height)
    node.children && _drawRect(node.children, ctx)
  })
}

function drawRect () {
  let canvas = document.querySelector('canvas')
  canvas.width = WIDTH
  canvas.height = HEIGHT
  let ctx = canvas.getContext('2d')
  _drawRect(NODE_LIST, ctx)
}

function calcClickPosition (event) {
    let canvas = document.querySelector('canvas')
  let rect = canvas.getBoundingClientRect()
  let x = (event.clientX - rect.left) / rect.width * WIDTH
  let y = (event.clientY - rect.top) / rect.height * HEIGHT
  return { x, y }
}

function isInRect(pos, node) {
  let { x, y, width: w, height: h } = node
  console.log(pos, x, y, pos.x >= x)
  return pos.x >= x && pos.x <= x + w && pos.y >= y && pos.y <= y + h
}

// 递归查找
function findClickNode (pos, nodeList) {
  for (let node of nodeList) {
    if (isInRect(pos, node)) {
      let childTarget = findClickNode(pos, node.children || [])
      return childTarget || node
    }
  }
  return null
}

function click (event) {
  let pos = calcClickPosition(event)
  let text = '点击区域不在画布'
  if (isInRect(pos, {x:0, y: 0, width: WIDTH, height: HEIGHT})) {
    let node = findClickNode(pos, NODE_LIST)
    text = node ? `点击了${node.text}` : '点击了画布' 
  }
  let el = document.querySelector('.show-click')
  el.innerText = text
}

drawRect()
document.addEventListener('click', click)

感谢邀请,虽然我不是 canvas 或者计算机图形学方面的高手,但是可以提供一点思路,仅供参考。

图形学上,判断一个点是否在一个封闭图形内部,一种方法是从这个点向外画一条射线,如果射线与该封闭图形轮廓的交叉点数量为奇数,那么这个点就在封闭图形内部,不过需要注意的是,对于相切的点要当成两个点来处理,且在计算开始前需要判断点是否在封闭图形的轮廓上。如果采用这种算法,大致就是求解两个集合的交集,每个交集为一个点,再判断点是否为相切点,然后得到交叉点数量,判断点是否在封闭图形内部。对于图形轮廓线自身有交叉的情况,还要分非零环绕原则和奇偶环绕原则另行讨论。
当然,如果封闭图形的形状是规则图形的话,可以利用几何特性判断点是否在封闭图形内部,这是初中几何就能搞定的问题。
而 canvas 2D 绘图上下文已经提供了一个叫 .isPointInPath 的方法来进行判断,接口的用法和示例代码参见 MDN文档:CanvasRenderingContext2D.prototype.isPointInPath

对于图形嵌套的问题,可以分为物理上的嵌套和逻辑上的嵌套来讨论。物理嵌套比较复杂,需要判断图形A与图形B所包含的点集合是否存在包含关系,相信在大多数图形渲染库中,物理嵌套关系的判断都是给复杂图形确定一个矩形外框,然后根据两个外框的嵌套关系来确定原图形嵌套。
逻辑嵌套,就是直接指定两个图形的关系,比如A在B内部、B在A内部、A在B外部这三种逻辑关系。

在确定了点与图形、图形与图形之间的关系之后,就可以判断点在哪些图形的内部了,这些都是很基础的逻辑知识,稍有常识的人都能判断。

虽然这其中每一个知识点都是极其简单的,但是代码实现较为纷繁,可以优化的地方也不少。题主可以根据以上拙见,仔细斟酌,多写代码,只要肯下功夫,终能实现。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏