根据布尔变量更新 A-Frame 中的事件侦听器

时间:2021-04-28 08:16:19

标签: typescript event-handling aframe

我正在尝试使用 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
}

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

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

谢谢大家。

1 个答案:

答案 0 :(得分:1)

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

现在处于更新,即

<块引用>

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

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

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

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

event.target.components['component-name'].data 如果您没有指定架构属性的名称,而只使用名为 single property component

的东西,这将起作用

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

同样在组件内部,您可以自由更改 trigger 变量 - 但不是与架构相关的变量,而是您将在 init let trigger = this.data.trigger; 中为架构创建的变量需要使用 el.setAttribute('click-handler', true) 但我不知道它是否会重新运行 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>

如果这有帮助,请告诉我。