webvr-polyfill
webvr-polyfill copied to clipboard
Cardboard UI does not work if application prevents translation of touch events into mouse events
Cardboard UI relies on the browser automatic translation of touch events into mouse click
events. If the application touchstart
handler calls event.preventDefault()
, this translation is disabled and Cardboard UI buttons do not function.
A fix could be to also listen for touchstart
in CardboardUI.prototype.listen
and take client(X|Y)
from event.touches[0].client(X|Y)
if it is defined.
You could also bind to a particular DOM element instead of the window.
On Tue, Nov 15, 2016 at 2:33 AM Jan Wrobel [email protected] wrote:
Cardboard UI relies on the browser automatic translation of touch events into mouse click events. If the application touchstart handler calls event.preventDefault(), this translation is disabled and Cardboard UI buttons do not function.
A fix could be to also listen for touchstart in CardboardUI.prototype.listen and take client(X|Y) from event.touches[0].client(X|Y) if it is defined.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/googlevr/webvr-polyfill/issues/174, or mute the thread https://github.com/notifications/unsubscribe-auth/AAQ8gNiHOS_bl1aLE_8nPiUJUuygR1q1ks5q-YplgaJpZM4KyXfc .
I'm not sure this is possible, CardboardUI
buttons are rendered in WebGL and do not have corresponding DOM elements, so CardboardUI
needs to listen on canvas for click
events. Application with custom touch navigation also needs to listen on the same canvas.
如果 canvas 的容器 添加了 pointerEvents: "none" CardboardUI 也不会添加事件响应
I think @borismus meant that the touch events that stop propagation be bound to particular DOM elements rather than interfering with the canvas element's events.
If I understand this correctly, there are a few work-arounds applications can implement, like not disrupting events while in VR mode. I think it's reasonable that the application is responsible for not interfering with these events making it to the canvas.
Thank you for filing!
@jsantell Please consider reopening this issue.
As @wrr pointed out, the standard way handle both touch and mouse events together is to add handlers for both events, and to call preventDefault
on touch events (see https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/).
More so, if an app is relying on third party libraries for processing touch/mouse events which are following this standard practice, it is not necessarily easy to make sure that preventDefault
is not called while in VR mode. In my case, I am using a gesture library that processes touch/mouse events on a parent element of the canvas, and calls preventDefault
without stopping propagation (so all events propagate to the canvas, while I handle the derived gesture events, and I want the gesture library to continue working while presenting with webvr). Also, preventDefault
on touch events is necessary to avoid several undesired browser side effects (such as scrolling or zooming), in addition to avoiding the compatibility 'click' events. I don't see a good way to work around this issue aside from updating webvr-polyfill to support touch + mouse events.
Here is a solution:
var kButtonWidthDp = 28;
var kTouchSlopFactor = 1.5;
CardboardUI.prototype.listen = function(optionsCallback, backCallback) {
var canvas = this.gl.canvas;
var hasTouchEventClass = typeof TouchEvent !== 'undefined';
this.listener = function(event) {
var midline = canvas.clientWidth / 2;
var buttonSize = kButtonWidthDp * kTouchSlopFactor;
var e = hasTouchEventClass && event instanceof TouchEvent ?
(event.preventDefault(), event.changedTouches[0]) : event;
// Check to see if the user clicked on (or around) the gear icon
if (e.clientX > midline - buttonSize &&
e.clientX < midline + buttonSize &&
e.clientY > canvas.clientHeight - buttonSize) {
event.preventDefault();
event.stopImmediatePropagation();
optionsCallback(event);
}
// Check to see if the user clicked on (or around) the back icon
else if (e.clientX < buttonSize && e.clientY < buttonSize) {
event.preventDefault();
event.stopImmediatePropagation();
backCallback(event);
}
};
canvas.addEventListener('click', this.listener, false);
canvas.addEventListener('touchstart', this.listener, false);
};
I'll reopen this and give it a further look and follow up next week -- thanks for the additional info, @speigg