【发布时间】:2011-04-28 04:46:23
【问题描述】:
假设我有一个 html5/canvas 应用程序,我可以在其中将对象放置在绘图画布上。某种图表编辑器,类似于 Visio(但更简单)
是否有一个框架可以帮助我找到单击/拖动的对象?
一个选项是捕获点击事件并遍历我的所有对象(以半智能方式)并检查它是否被点击/拖动,但是我讨厌重新发明轮子!
【问题讨论】:
标签: javascript html canvas frameworks dom-events
假设我有一个 html5/canvas 应用程序,我可以在其中将对象放置在绘图画布上。某种图表编辑器,类似于 Visio(但更简单)
是否有一个框架可以帮助我找到单击/拖动的对象?
一个选项是捕获点击事件并遍历我的所有对象(以半智能方式)并检查它是否被点击/拖动,但是我讨厌重新发明轮子!
【问题讨论】:
标签: javascript html canvas frameworks dom-events
不幸的是,您渲染到画布上的东西没有对象模型。当您检测到单击、鼠标上移、鼠标下移、拖动时,您需要跟踪您绘制的所有内容并触发单独的事件。这意味着为所有上下文方法创建一个包装器,将线条/图像及其属性存储到 COM(Canvas 对象模型:) 并为每个线条/图像触发事件
我还没有看到任何东西可以做到这一点。这可能是很多的工作,每次我需要向画布添加交互性时,我都会编写自定义代码
【讨论】:
对于 Visio 风格的应用程序,或与绘制对象的交互性很重要的任何应用程序,您最好使用 SVG。已经有 some open source projects 可以用作起点。 SVG 的问题在于,与任何 DOM 重的东西一样,一旦您有大量对象要操作,性能就会急剧下降。
【讨论】:
简而言之,Canvas 本身不会跟踪这些。
如果你不想重新发明轮子,可以从教程"Making and Moving Selectable Shapes on an HTML5 Canvas: A Simple Example."我的示例代码开始,它提供了一个很好的介绍和点击和拖动的平台。
【讨论】:
【讨论】:
您实际上可以使用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>
【讨论】: