【问题标题】:Is it possible to use HTML image maps within an A-Frame scene?是否可以在 A-Frame 场景中使用 HTML 图像映射?
【发布时间】:2021-02-17 07:12:08
【问题描述】:

场景如下:

我有一张世界地图的 360º 全景图像,在 A-Frame 上运行

我希望每个大陆都可以点击,重定向到相关的维基百科页面。

我尝试了什么:

  • 我尝试像在普通项目中一样使用<map> 标签,但是 没有结果。
  • 我也阅读了文档,但什么也没找到 与此相关。

我的问题:

我的目标可能吗?如果有,怎么做?

我的代码:

注意:到目前为止,只有非洲大陆有坐标。

JSFiddle

<script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>

<a-scene>
  <a-entity camera look-controls="reverseMouseDrag: true"></a-entity>
  <a-sky id="image-360" radius="10" src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Equirectangular_projection_SW.jpg/1920px-Equirectangular_projection_SW.jpg" usemap="#image_map"></a-sky>
  
  <map name="image_map">
    <area alt="Africa" title="Africa" href="https://en.wikipedia.org/wiki/Africa" coords="920,296 871,351 873,424 916,461 979,449 1014,463 1005,490 1032,540 1020,579 1056,663 1081,667 1114,655 1146,600 1175,561 1165,504 1220,451 1228,422 1191,426 1124,314 1063,308 1054,324 1014,302 1016,284 963,290" shape="polygon">
  </map>
  
</a-scene>

【问题讨论】:

    标签: hyperlink aframe imagemap


    【解决方案1】:

    &lt;map&gt; 标签将区域置于图像上方。它不会像那样将区域投影到三维对象上。

    我建议您在 a-frame 自定义组件中重复使用 &lt;map&gt; 标记中定义的区域。

    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 属性配合使用,而且&lt;a-image&gt; 也会发出带有点击区域名称的信号。

    您可以看到它与 3D 平面和圆柱体一起使用 here

    tldr 结束

    0。收集&lt;map&gt;数据

    简单的解析。获取&lt;map&gt; 元素,遍历所有子元素,并使用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 那样抓取点击的坐标
    • 隐藏场景
    • 看看哪个&lt;area&gt;document.elementFromPoint(x, y);的坐标处。
    • 展示场景
    • document.createEvent("MouseEvent");创建鼠标事件
    • &lt;area&gt; 元素上发送它。

    隐藏/显示技巧即使在我的手机上也非常有效。我真的很惊讶场景没有闪烁、冻结,甚至没有减速。

    但是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);
        }
      }
    })
    

    【讨论】:

    • 这确实是 hacky。但它完成了工作!但它在 Firefox 中不起作用。知道为什么吗?
    • @MikeMichaels 没有注意到,似乎elementFromPoint 没有在 Firefox 上抓取区域。不知道为什么,但似乎最好找到一个非hacky的解决方案,因为它太复杂了:)
    • @MikeMichaels 由于该解决方案不仅很老套,而且您注意到完全不可靠 - 我已经编辑了我的答案,并基于一个应该适用于任何浏览器/平台的解决方案。让我知道你是否喜欢它。此外,这里已经很晚了,所以如果有什么不清楚或编辑不当,请随时指出。
    猜你喜欢
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-29
    相关资源
    最近更新 更多