首页 > 解决方案 > 根据布尔变量更新 A-Frame 中的事件侦听器

问题描述

我正在尝试使用 A-Frame 创建一个 Typescript 类,该类允许我渲染模型并在场景中插入元素以响应点击。处理程序应受用作触发器的布尔变量的值的约束;具体来说,仅当此触发器的值设置为 true 时,该类才应插入一个元素。

我的方法是基于组件的注册,在其中我将处理程序的定义插入到更新函数中。这是代码的草稿:

AFRAME.registerComponent('click-handler', {
  schema: {
    trigger: { type: 'boolean' },
  },

  // init function definition

  update: function () {
    let mouseDownTime: number = null;
    let mouseDownPoint: any = null;
    let trigger = this.data.trigger;

    function clickHandler(event) {
      let point = event.detail.intersection.point;
      let pointString = point.x.toFixed(3) + " " + point.y.toFixed(3) + " " + point.z.toFixed(3);

      // compute the box that contains the model
      let model = <any>document.getElementById("model")
      const box = new THREE.Box3().setFromObject(model.object3D);
      const boxSizes = box.getSize(new THREE.Vector3());

      if (trigger) {
        // compute the min size of the box (x, y, z)
        // it will be used to set pointer radius
        let minBoxSize = Math.min(boxSizes.x, boxSizes.y, boxSizes.z);
        let radius = minBoxSize / 30;

        let scene = document.getElementById("scene");
        var marker = document.createElement("a-sphere");
        scene.appendChild(marker);

        marker.setAttribute("class", "pointer");
        marker.setAttribute("radius", `${radius}`);
        marker.setAttribute("color", "#CC0000");
        marker.setAttribute("position", pointString);
      }
    }

    this.el.addEventListener('mousedown', event => {
      mouseDownTime = new Date().getTime();
      mouseDownPoint = event.detail.intersection.point;
    });

    this.el.addEventListener('mouseup', event => {
      if (!event.detail.intersection) return;

      let mouseUpTime = new Date().getTime();
      let mouseUpPoint = event.detail.intersection.point;

      // compute the differences (time and position) between press and release
      let timeDiff = mouseUpTime - mouseDownTime;

      // if press and release occur on the same point within 185 ms
      //  we consider the overall "event" as a click
      if (timeDiff <= 185 && JSON.stringify(mouseDownPoint) === JSON.stringify(mouseUpPoint)) {
        clickHandler(event);
      }
    });
  },

  // remove function definition
}

我想了解的是是否可以更新事件侦听器,以便它考虑到触发器的新值。恐怕当前代码总是插入新的事件侦听器,这应该是我的问题的原因。

编辑:在 Angular 组件模板中,我有一个按钮;当它被按下时,它将“外部触发器”设置为true. 我正在尝试做的是找到一种方法让处理程序意识到这种变化。

谢谢你们。

标签: typescriptevent-handlingaframe

解决方案


我认为您应该将 addEventListener 放在init中。

现在它正在更新

在组件初始化和组件的任何属性更新(例如,通过 setAttribute)时调用。用于修改实体。来源

因此,每次发生变化时,您最终都会添加一个事件侦听器。

然后您可能应该在事件侦听器内部检查您的标志是否已设置。在事件侦听器中,您应该能够通过以下方式访问变量:

event.target.components['component-name'].data.trigger对于属性本身。

event.target.components['component-name'].data如果您不指定架构属性的名称,这将起作用,而只使用称为单属性组件的东西

event.target.components['component-name'].variable对于组件内的变量或 this.variable

同样在组件内部,您可以自由更改触发器变量 - 但不是与架构相关的变量,而是您将在 initlet trigger = this.data.trigger;中为您需要使用的架构创建的变量,el.setAttribute('click-handler', true)但我不知道它是否会重新运行 init部分与否。编辑:这将运行更新 - 而不是 init。

工作示例:

左上角有一个按钮来切换球体的可点击性。

如果您不想使用变量,这就是您可以在事件侦听器中访问组件值的方式。 event.target.components['click-handler'].trigger

function toggleClickHandler() {
  let el = document.getElementById("clickableElement");
  // here you can set the new value for the component
  // also look how you can access component variable from outside
  // changing the value will trigger the 'update' lifecycle function 
  el.setAttribute("click-handler", "trigger", !el.components['click-handler'].trigger);
  console.log(el.components['click-handler'].trigger);
}
<!DOCTYPE html>
<html>
  <head>
    <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
  </head>
  <body>
    <!-- script here because on stack JS is after the HTML --> 
    <script>
      AFRAME.registerComponent('click-handler', {
        schema: {
          // if you specify name you have to use it when setting the attribute
          trigger: { type: 'boolean' },
        },
        // init function definition
        init: function() {
          this.el.addEventListener('mousedown', (event) => {
            if (!this.trigger) return;
            // ... your logic ...
            console.log("I was mousedowned!");
            this.el.setAttribute('material', 'color', 'green');
          });

          this.el.addEventListener('mouseup', (event) => {
            if (!this.trigger) return;
            // ... your logic ...
            console.log("I was mouseuped!");
            this.el.setAttribute('material', 'color', 'red');
          });
        },
        // update is called once on init and every time the elements attribute is changed.
        update: function() {
          this.trigger = this.data.trigger;
        }
      });
    </script>
    <a-scene>
      <a-entity camera look-controls>
        <a-entity cursor="fuse: true; fuseTimeout: 500;"
                  position="0 0 -1"
                  geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
                  material="color: black; shader: flat">
        </a-entity>
      </a-entity>
      <a-sphere id="clickableElement" scale="1 2 3" position="6 1 -5" click-handler="trigger: true">
      </a-sphere>
    </a-scene>
    <button onclick="toggleClickHandler()" style="width: 100px; height: 100px; position: absolute; top: 0; left: 0;" value="toggle"></button>
  </body>
</html>

让我知道这是否有帮助。


推荐阅读