mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-07 00:02:53 +08:00
119 lines
4.7 KiB
TypeScript
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);
|
|
}
|
|
}
|