Files
cocos-engine/pal/input/web/accelerometer-input.ts

119 lines
4.7 KiB
TypeScript

import { AccelerometerCallback } from 'pal/input';
import { systemInfo } from 'pal/system-info';
import { screenAdapter } from 'pal/screen-adapter';
import { EventTarget } from '../../../cocos/core/event/event-target';
import { BrowserType, OS } from '../../system-info/enum-type';
import { EventAcceleration, Acceleration } from '../../../cocos/input/types';
import { InputEventType } from '../../../cocos/input/types/event-enum';
export class AccelerometerInputSource {
private _intervalInMileSeconds = 200;
private _accelTimer = 0;
private _eventTarget: EventTarget = new EventTarget();
private _deviceEventName: 'devicemotion' |'deviceorientation';
private _globalEventClass: typeof window.DeviceMotionEvent | typeof window.DeviceOrientationEvent;
private _didAccelerateFunc: (event: DeviceMotionEvent | DeviceOrientationEvent) => void;
constructor () {
// init event name
this._globalEventClass = window.DeviceMotionEvent || window.DeviceOrientationEvent;
// TODO fix DeviceMotionEvent bug on QQ Browser version 4.1 and below.
if (systemInfo.browserType === BrowserType.MOBILE_QQ) {
this._globalEventClass = window.DeviceOrientationEvent;
}
this._deviceEventName = this._globalEventClass === window.DeviceMotionEvent ? 'devicemotion' : 'deviceorientation';
this._didAccelerateFunc = this._didAccelerate.bind(this);
}
private _registerEvent () {
this._accelTimer = performance.now();
window.addEventListener(this._deviceEventName, this._didAccelerateFunc, false);
}
private _unregisterEvent () {
this._accelTimer = 0;
window.removeEventListener(this._deviceEventName, this._didAccelerateFunc, false);
}
private _didAccelerate (event: DeviceMotionEvent | DeviceOrientationEvent) {
const now = performance.now();
if (now - this._accelTimer < this._intervalInMileSeconds) {
return;
}
this._accelTimer = now;
let x = 0;
let y = 0;
let z = 0;
if (this._globalEventClass === window.DeviceMotionEvent) {
const deviceMotionEvent = event as DeviceMotionEvent;
const eventAcceleration = deviceMotionEvent.accelerationIncludingGravity;
x = (eventAcceleration?.x || 0) * 0.1;
y = (eventAcceleration?.y || 0) * 0.1;
z = (eventAcceleration?.z || 0) * 0.1;
} else {
const deviceOrientationEvent = event as DeviceOrientationEvent;
x = ((deviceOrientationEvent.gamma || 0) / 90) * 0.981;
y = -((deviceOrientationEvent.beta || 0) / 90) * 0.981;
z = ((deviceOrientationEvent.alpha || 0) / 90) * 0.981;
}
if (screenAdapter.isFrameRotated) {
const tmp = x;
x = -y;
y = tmp;
}
// TODO: window.orientation is deprecated: https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
// try to use experimental screen.orientation: https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation
const PORTRAIT = 0;
const LANDSCAPE_LEFT = -90;
const PORTRAIT_UPSIDE_DOWN = 180;
const LANDSCAPE_RIGHT = 90;
const tmpX = x;
if (window.orientation === LANDSCAPE_RIGHT) {
x = -y;
y = tmpX;
} else if (window.orientation === LANDSCAPE_LEFT) {
x = y;
y = -tmpX;
} else if (window.orientation === PORTRAIT_UPSIDE_DOWN) {
x = -x;
y = -y;
}
// fix android acc values are opposite
if (systemInfo.os === OS.ANDROID
&& systemInfo.browserType !== BrowserType.MOBILE_QQ) {
x = -x;
y = -y;
}
const timestamp = performance.now();
const acceleration = new Acceleration(x, y, z, timestamp);
const eventAcceleration = new EventAcceleration(acceleration);
this._eventTarget.emit(InputEventType.DEVICEMOTION, eventAcceleration);
}
public start () {
// for iOS 13+, safari
if (window.DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === 'function') {
DeviceMotionEvent.requestPermission().then((response) => {
if (response === 'granted') {
this._registerEvent();
}
}).catch((e) => {});
} else {
this._registerEvent();
}
}
public stop () {
this._unregisterEvent();
}
public setInterval (intervalInMileSeconds: number) {
this._intervalInMileSeconds = intervalInMileSeconds;
}
public on (eventType: InputEventType, callback: AccelerometerCallback, target?: any) {
this._eventTarget.on(eventType, callback, target);
}
}