UNPKG

lesca-sensor-motion

Version:

collect `device-motion` to a useful value. Include user permission and event listener.

158 lines (143 loc) 4.65 kB
import MobileDetect from 'mobile-detect'; const { navigator, location } = window; const { userAgent } = navigator; const { protocol } = location; enum Permission { granted = 'granted', denied = 'denied', } enum STATUS { unset = 'unset', desktop = 'desktop is not support', ssl = 'https require', userDenied = 'user denied', denied = 'motion not support!', } const defaultAccelerationIncludingGravity: DeviceMotionEventAcceleration = { x: 0, y: 0, z: 0, }; export default class Motion { public disable: boolean; public isSupported: boolean; public delay: number; public each: number; private isQueue: boolean; private force: number; private sum: DeviceMotionEventAcceleration | null; private sum2: DeviceMotionEventAcceleration | null; private bindFunction: (e: DeviceMotionEvent) => void; private callback: Function; private call: (e: DeviceMotionEvent) => void; private error: () => void; private sync: () => void; private get: () => 'mobile' | 'desktop'; private queue: NodeJS.Timeout | null; /** * new Motion(1000, 50); * @param {number} delay if callback called, the listener will stop as delay time; * @param {number} each time of each frame */ constructor(delay: number = 1000, each: number = 50) { this.disable = true; // use for stop listen not destroy this.isSupported = false; // check devicemotion support? this.delay = delay; // if callback called, the listener will stop as delay time; this.each = each; // time of each frame this.isQueue = true; // for delay use. this.sum = defaultAccelerationIncludingGravity; this.sum2 = defaultAccelerationIncludingGravity; this.force = 20; this.queue = null; this.bindFunction = (e: DeviceMotionEvent) => {}; this.callback = (e: number) => console.log(e); this.call = (e: DeviceMotionEvent) => { this.sum = e.accelerationIncludingGravity; }; this.error = () => console.log(STATUS.denied); this.sync = () => { if (!this.disable) return; if (this.sum !== null && this.sum2 !== null) { if ( typeof this.sum.x === 'number' && typeof this.sum.y === 'number' && typeof this.sum.z === 'number' && typeof this.sum2.x === 'number' && typeof this.sum2.y === 'number' && typeof this.sum2.z === 'number' ) { const x = Math.abs(this.sum.x - this.sum2.x); const y = Math.abs(this.sum.y - this.sum2.y); const z = Math.abs(this.sum.z - this.sum2.z); const c = Math.abs(x + y + z); if (c > this.force) { if (!this.isQueue) return; this.isQueue = false; this.callback(c); this.sum2 = this.sum = { x: 0, y: 0, z: 0 }; setTimeout(() => (this.isQueue = true), this.delay); } this.sum2 = this.sum; } } }; this.get = () => { const m = new MobileDetect(userAgent); if (m.tablet() || m.mobile()) return 'mobile'; return 'desktop'; }; } /** * initial on click event * @returns */ permission() { return new Promise((res, rej) => { //desktop escape all if (this.get() === 'desktop') { rej(STATUS.desktop); } // IOS 14+ need permission request. if (typeof (DeviceMotionEvent as any).requestPermission === 'function') { // ISO need SSL also. if (protocol.indexOf('https') < 0) rej(STATUS.ssl); (DeviceMotionEvent as any) .requestPermission() .then((permissionState: string) => { if (permissionState === Permission.granted) { this.isSupported = true; res(permissionState); } else { this.isSupported = false; rej(STATUS.userDenied); } }) .catch(this.error); } else { this.isSupported = true; res(Permission.denied); } }); } /** * add listener * @param {number} force a value for shake force * @param {function} callback */ addEventListener(force: number = 20, callback: Function) { this.force = force; this.isQueue = true; this.bindFunction = this.call.bind(this); if (callback) this.callback = callback; this.sum = this.sum2 = { x: 0, y: 0, z: 0 }; window.addEventListener('devicemotion', this.bindFunction); this.queue = setInterval(() => this.sync(), this.each); } /** * remove Events */ destroy() { window.removeEventListener('devicemotion', this.bindFunction); if (this.queue) clearInterval(this.queue); } }