<map> 标签将区域置于图像上方。它不会像那样将区域投影到三维对象上。
我建议您在 a-frame 自定义组件中重复使用 <map> 标记中定义的区域。
tldr:
我制作了a component,它似乎可以完成这项工作,而且使用起来相当简单:
<a-image material="src: #texture" asourcemap="#some-map"></a-image>
<!-- somewhere else -->
<map id="some-map">
<area shape="rect" alt="rectangle" coords="0,0, 50,50" href="rect.html">
<area shape="polygon" alt="poly" coords="0,0, 40,50, 25,0">
</map>
它应该可以很好地与href 属性配合使用,而且<a-image> 也会发出带有点击区域名称的信号。
您可以看到它与 3D 平面和圆柱体一起使用 here。
tldr 结束
0。收集<map>数据
简单的解析。获取<map> 元素,遍历所有子元素,并使用getAttribute() 收集他们的数据:
var map = document.querySelector(selector);
for (let area of map.children) {
// area.getAttribute("href") - href attribute
// area.getAttribute("alt") - alt name
// area.getAttribute("coords") - coordinates array.
}
存储它们以备后用。坐标是逗号分隔的字符串,所以你可能需要parseInt()他们,管理顺序(即[[x1,x1], [x2,y2], [x3, y3]])
1.使框架实体可交互
对点击做出反应,更重要的是 - 检查点击发生的位置:
this.el.addEventListener("click", evt => {
var UVPoint = evt.detail.intersection.uv
})
UV mapping 将帮助我们确定纹理上的哪个点被点击。 UV 范围为 ,因此我们需要重新缩放 UVPoint:
// may need waiting for "model-loaded"
let mesh = this.el.getObject3D("mesh")
// this may not be available immidiately
let image = mesh.material.map.image
let x_on_image = UVPoint * image.width
// the y axis goes from <1, 0>
let y_on_image = image.height - UVPoint * image.heigth
嘿,我们得到了区域坐标和点坐标!
只剩下一件事了:
2。判断一个区域是否被点击
这里不需要重新发明轮子。 This SO question on checking if a point is inside a polygon 有一个简单的 inside(point, polygon) 函数。实际上,我们拥有所需的一切,所以我们要做的最后一件事是:
- 遍历多边形
- 检查点击的点是否在任何多边形内
- 如果是肯定的 - 做你的事
像这样:
var point = [x_on_texture, y_on_texture]
for (var i = 0; i < polygons.length; i++) {
// polygons need to be [[x1, y1], [x2, y2],...[xn, yn]] here
if (inside(point, polygons[i]) {
console.log("polygon", i, "clicked!")
}
}
如果您跳过了 tldr 部分 - 上述步骤在this component 中组合并在this example 中使用
3.旧的,hacky 尝试
另一种方法是:
- 收到对 a-frame 实体的点击
- 像 1 那样抓取点击的坐标
- 隐藏场景
- 看看哪个
<area>在document.elementFromPoint(x, y);的坐标处。
- 展示场景
- 用
document.createEvent("MouseEvent");创建鼠标事件
- 在
<area> 元素上发送它。
隐藏/显示技巧即使在我的手机上也非常有效。我真的很惊讶场景没有闪烁、冻结,甚至没有减速。
但是document.elementFromPoint(x, y); 不能与 Firefox 一起使用,并且可能任何使其工作的尝试都比 0-2 步骤更耗时。此外,我相信陷阱会变得更大并取决于具体情况。
无论如何,这是 old-answer 组件:
/* SETUP
<a-scene>
<a-image press-map>
</a-scene>
<image id="image" sourcemap="map">
<map name="map">
<area ...>
</map>
*/
AFRAME.registerComponent("press-map", {
init: function() {
// the underlying image
this.img = document.querySelector("#image")
// react on clicks
this.el.addEventListener("click", evt => {
// get the point on the UV
let uvPoint = evt.detail.intersection.uv
// the y is inverted
let pointOnImage = {
x: uvPoint.x * this.img.width,
y: this.img.height - uvPoint.y * this.img.height
}
// the ugly show-hide bits
this.el.sceneEl.style.display = "none";
this.img.style.display = "block";
// !! grab the <area> at the (x,y) position
var el = document.elementFromPoint(pointOnImage.x, pointOnImage.y);
this.el.sceneEl.style.display="block"
this.img.style.display="none"
// create and dispatch the event
var ev = document.createEvent("MouseEvent");
ev.initMouseEvent(
"click",
true /* bubble */, false /* cancelable */,
window, null,
x, y, 0, 0, /* coordinates */
false, false, false, false, /* modifier keys */
0 /*left*/, null
);
el.dispatchEvent(ev);
}
}
})