【问题标题】:Find out which object was clicked on a html5 Canvas找出在 html5 画布上单击了哪个对象
【发布时间】:2011-04-28 04:46:23
【问题描述】:

假设我有一个 html5/canvas 应用程序,我可以在其中将对象放置在绘图画布上。某种图表编辑器,类似于 Visio(但更简单)

是否有一个框架可以帮助我找到单击/拖动的对象?

一个选项是捕获点击事件并遍历我的所有对象(以半智能方式)并检查它是否被点击/拖动,但是我讨厌重新发明轮子!

【问题讨论】:

    标签: javascript html canvas frameworks dom-events


    【解决方案1】:

    不幸的是,您渲染到画布上的东西没有对象模型。当您检测到单击、鼠标上移、鼠标下移、拖动时,您需要跟踪您绘制的所有内容并触发单独的事件。这意味着为所有上下文方法创建一个包装器,将线条/图像及其属性存储到 COM(Canvas 对象模型:) 并为每个线条/图像触发事件

    我还没有看到任何东西可以做到这一点。这可能是很多的工作,每次我需要向画布添加交互性时,我都会编写自定义代码

    【讨论】:

      【解决方案2】:

      对于 Visio 风格的应用程序,或与绘制对象的交互性很重要的任何应用程序,您最好使用 SVG。已经有 some open source projects 可以用作起点。 SVG 的问题在于,与任何 DOM 重的东西一样,一旦您有大量对象要操作,性能就会急剧下降。

      【讨论】:

      • 什么是“大数”? 100? 10.000?
      • 使用 excanvas 绘制超过 10,000 个点时,我的性能很慢
      • @Dribbel 它会根据浏览器和机器规格而有所不同,如果您的目标是具有现代浏览器的桌面,那么超过 10,000,如果您使用手机,那么可能更像 1000,如果您'正在尝试使用 VML 在 IE 中工作,可能有几百个。
      • @JuanMendes IIRC,excanvas 是基于 VML,它是微软较旧、较慢的 SVG 版本
      【解决方案3】:

      简而言之,Canvas 本身不会跟踪这些。

      如果你不想重新发明轮子,可以从教程"Making and Moving Selectable Shapes on an HTML5 Canvas: A Simple Example."我的示例代码开始,它提供了一个很好的介绍和点击和拖动的平台。

      【讨论】:

        【解决方案4】:

        【讨论】:

          【解决方案5】:

          您实际上可以使用ctx.isPointInPath() 方法很容易地做到这一点。它采用x 坐标和y 坐标形式的点,并检查画布中是否有路径包含该点。

          那么,您如何使用它来确定该点属于哪个元素?好吧,当画布被清除时,画布上显然不会留下任何点,因此ctx.isPointInPath()总是返回false。然后,我们开始将每个元素一个一个地添加到画布上。在画布上绘制元素后,我们检查函数 - 如果它返回 false,则该点不在该元素内,因此我们继续。

          自从画布为空白以来,我们一直在检查每个元素,我们知道该点不可能在我们已经检查过的元素之一内。因此,当我们最终找到返回 true 的元素时,我们知道该点必须在该特定元素内。

          这里的好处是我们不必编写任何自定义代码来尝试检测点是否在形状内,这可能会很棘手,特别是如果形状很复杂 - 这一切都由浏览器处理。

          这种方法存在一个问题 - 如果该点在两个或多个形状内,那么它只会报告它在第一个形状内。一个解决方案可能是拥有一个单独的隐藏画布并遍历所有形状,在每个形状之后将其清除,并将ctx.isPointInPath() 返回 true 的名称存储在一个数组中。不过,您可能还可以采取其他方法。在这个答案中,我更多地关注基本思想,而不是这样的特殊情况。


          下面我有一个实际使用这种方法的示例。我已经设置了一个画布、一个跟踪鼠标位置的脚本和一个对象列表,每个对象都有一个名称,以及一个将其绘制到画布上的绘制函数。你可以想象一个面向对象的方法,它只是一个对象列表,所有这些对象都扩展了一些 AbstractShape 类,但我为演示保持了简单。

          然后我只是不断地重新绘制画布(就像你在制作动画、制作游戏等时所做的那样),并且每次都使用我上面定义的算法来检查鼠标指针是否处于该形状。

          //Define the objects to draw on the canvas
          let objects = [
            {"name":"Circle", "draw":()=>{
              ctx.beginPath();
              ctx.arc(200, 75, 50, 0, 2 * Math.PI);
              ctx.fill();
              ctx.closePath();
            }},
            {"name":"Square", "draw":()=>{
               ctx.rect(10, 10, 100, 100);
               ctx.fill();
            }}
          ];
          
          //Keep track of the mouse pointer
          let mouseX, mouseY;
          document.body.addEventListener("mousemove", (event)=>{
            mouseX = event.clientX;
            mouseY = event.clientY;
          });
          
          //Find the canvas, its context, and the output element
          let cv = document.getElementById("cv");
          let ctx = cv.getContext("2d");
          let output = document.getElementById("output");
          
          //Draw the objects on the canvas
          function draw() {
          
            //Clear the canvas
            ctx.clearRect(0, 0, cv.width, cv.height);
            
            //Clear the output div
            output.innerHTML = "";
            
            //This variable is true until we find an object
            let noObjectFound = true;
            
            //Loop through all of the objects
            for (let object of objects) {
              //Draw the current object
              object.draw();
              //Check if the mouse is on top of it
              if (noObjectFound && ctx.isPointInPath(mouseX, mouseY)) {
                //Update the output div
                output.innerHTML = object.name;
                noObjectFound = false;
              }
            }
            
            //Call again ASAP
            window.requestAnimationFrame(draw);
          }
          
          window.requestAnimationFrame(draw);
          body {
            padding:0;
            margin:0;
          }
          
          #cv {
            background-color:#eee;
          }
          <canvas id="cv"></canvas>
          <h1 id="output"></h1>

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-05
            • 1970-01-01
            相关资源
            最近更新 更多