在WebVR/A-Frame中如何监听Android Chrome上的点击事件?

7

我正在尝试监听Android Chrome上的点击事件(确切地说是来自Google Cardboard的磁铁拉力),但是如果设备进入VR模式,则似乎无法正常工作。 我正在使用三星Galaxy S7进行测试:

JS:

window.addEventListener('click', function (evt) {
    console.log("test")
});

在三星的内置 Android 浏览器中,日志在 VR 模式下或非 VR 模式下都会输出。而在 Android Chrome 中,只有在浏览器不处于 VR 模式时才会显示日志。
HTML:
<a-entity camera="userHeight: 1.6" restrict-position 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-Frame版本0.7.0,但是这个问题也可以通过使用本地WebVR API来重现。
我认为画布可能会消耗点击事件,所以我尝试直接将事件监听器添加到画布中。这也没有起作用。
这是Chrome的一个bug吗?有什么解决方法吗?我只需要能够监听按钮按下事件。
2个回答

3

我终于找到了一个可行的方法。我们必须使用本地的WebVR APIs:

function addVRClickListener(clickCallback) {
    let lastButtonState = [];
    let presentingDisplay = null;

    // Set up a loop to check gamepad state while any VRDisplay is presenting.
    function onClickListenerFrame() {
      // Only reschedule the loop if a display is still presenting.
      if (presentingDisplay && presentingDisplay.isPresenting) {
        presentingDisplay.requestAnimationFrame(onClickListenerFrame);
      }
      
      let gamepads = navigator.getGamepads();
      for (let i = 0; i < gamepads.length; ++i) {
        let gamepad = gamepads[i];
        // Ensure the gamepad is valid and has buttons.
        if (gamepad &&
            gamepad.buttons.length) {
          let lastState = lastButtonState[i] || false;
          let newState = gamepad.buttons[0].pressed;
          // If the primary button state has changed from not pressed to pressed 
          // over the last frame then fire the callback.
          if (newState && !lastState) {
            clickCallback(gamepad);
          }
          lastButtonState[i] = newState;
        }
      }
    }

    window.addEventListener('vrdisplaypresentchange', (event) => {
      // When using the polyfill, CustomEvents require event properties to
      // be attached to the `detail` property; native implementations
      // are able to attach `display` directly on the event.
      var display = event.detail ? event.detail.display : event.display;
      if (display.isPresenting) {
        let scheduleFrame = !presentingDisplay;
        presentingDisplay = display;
        if (scheduleFrame)
          onClickListenerFrame();
      } else if (presentingDisplay == display) {
        presentingDisplay = null;
      }
    });
  }

  

然后我们可以注册点击事件:
  addVRClickListener(onClick);

我在A-frame的代码中看到的问题是它没有考虑到显示更改(非VR-> VR)来传播触摸事件。

这似乎是A-Frame中的一个错误。


这对你真的有效吗?我无法确定 Chrome 是否曾经支持 Cardboard 按钮并已将其删除,或者它是否曾经起作用。我的 Cardboard 按钮在 Android 应用程序中完美运行,但在 Chrome VR 模式下不会注册任何事件(而且在上面给出的函数中,游戏手柄是 4 个空格的列表)。在 VR 模式下,触摸事件也被禁用了。 - Kyle Baker
(只是想补充一下,我不使用磁性按钮,而是使用基于触摸的按钮,这应该排除了很多复杂性) - Kyle Baker

1
这似乎与我遇到的一些磁性按钮问题有关。 首先尝试的事情可能是使用touchstart而不是click: 您可以尝试将此组件添加到场景中,然后查看是否有效:
<script>
AFRAME.registerComponent("click-handler", {
    init: function() {
        const sceneEl = this.el.sceneEl
        const canvasEl = sceneEl.canvas    
        canvasEl.addEventListener('touchstart', () => {
            // code here
        })
    }
});
</script>

<a-scene click-handler>...</a-scene>

在这个主题中有更多的上下文信息: aframe 纸板按钮(磁铁)点击无法触发

接下来,如果那行不通,那么也许 chrome 标志和这个主题可以帮助:

在 Javascript 中检测 Google Cardboard 磁性按钮点击

chrome://flags/#enable-generic-sensor

chrome://flags/#enable-generic-sensor-extra-classes


我尝试了所有的建议...这是我发现的问题:
  1. 监听“click-handler”不起作用。没有事件被触发。
  2. 从磁层读取的传感器值非常不准确。例如,只要在任何X-Y-Z轴上倾斜手机就会产生多余的值,因为它正在尝试在所有坐标中读取磁场 - 没有真正区分手机运动和真实按钮按下的方法。更重要的是,我仍然需要用户能够手动触摸屏幕才能触发事件。
- TtT23
嗯,只是好奇,使用唐的通用控件是否适用于您的Cardboard和Android Chrome:https://github.com/donmccurdy/aframe-extras/tree/master/src/controls 如果不行,那可能更多地涉及Chrome和磁性按钮的问题。 - Noam Almosnino
尝试了Don的通用控件,它也不起作用。看源代码,它在幕后做的是监听touchstart事件,这是我已经尝试过的事情。 - TtT23

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接