diff --git a/src/reanimated2/NativeReanimated/NativeReanimated.ts b/src/reanimated2/NativeReanimated/NativeReanimated.ts index 04952384960..af11f02605a 100644 --- a/src/reanimated2/NativeReanimated/NativeReanimated.ts +++ b/src/reanimated2/NativeReanimated/NativeReanimated.ts @@ -1,5 +1,10 @@ import { NativeModules } from 'react-native'; -import { ShareableRef, ShareableSyncDataHolderRef } from '../commonTypes'; +import { + ShareableRef, + ShareableSyncDataHolderRef, + Value3D, + ValueRotation, +} from '../commonTypes'; import { LayoutAnimationFunction } from '../layoutReanimation'; import { version as jsVersion } from '../../../package.json'; @@ -88,7 +93,7 @@ export class NativeReanimated { registerSensor( sensorType: number, interval: number, - handler: ShareableRef + handler: ShareableRef | ((data: Value3D | ValueRotation) => void) ) { return this.InnerNativeModule.registerSensor(sensorType, interval, handler); } diff --git a/src/reanimated2/commonTypes.ts b/src/reanimated2/commonTypes.ts index 891e43cec2e..1f47538164f 100644 --- a/src/reanimated2/commonTypes.ts +++ b/src/reanimated2/commonTypes.ts @@ -145,6 +145,14 @@ export interface Animation extends AnimationObject { ) => void; } +export enum SensorType { + ACCELEROMETER = 1, + GYROSCOPE = 2, + GRAVITY = 3, + MAGNETIC_FIELD = 4, + ROTATION = 5, +} + export interface NumericAnimation { current?: number; } diff --git a/src/reanimated2/hook/index.ts b/src/reanimated2/hook/index.ts index 5f1d6a20e65..18dedcf9831 100644 --- a/src/reanimated2/hook/index.ts +++ b/src/reanimated2/hook/index.ts @@ -20,7 +20,7 @@ export { useAnimatedScrollHandler } from './useAnimatedScrollHandler'; export type { ScrollHandler, ScrollHandlers } from './useAnimatedScrollHandler'; export { useDerivedValue } from './useDerivedValue'; export type { DerivedValue } from './useDerivedValue'; -export { useAnimatedSensor, SensorType } from './useAnimatedSensor'; +export { useAnimatedSensor } from './useAnimatedSensor'; export { useFrameCallback } from './useFrameCallback'; export type { FrameCallback } from './useFrameCallback'; export { useAnimatedKeyboard } from './useAnimatedKeyboard'; diff --git a/src/reanimated2/hook/useAnimatedSensor.ts b/src/reanimated2/hook/useAnimatedSensor.ts index 04ad72a4d27..9dfb7deace2 100644 --- a/src/reanimated2/hook/useAnimatedSensor.ts +++ b/src/reanimated2/hook/useAnimatedSensor.ts @@ -1,14 +1,11 @@ import { useEffect, useRef } from 'react'; import { makeMutable, registerSensor, unregisterSensor } from '../core'; -import { SharedValue, Value3D, ValueRotation } from '../commonTypes'; - -export enum SensorType { - ACCELEROMETER = 1, - GYROSCOPE = 2, - GRAVITY = 3, - MAGNETIC_FIELD = 4, - ROTATION = 5, -} +import { + SensorType, + SharedValue, + Value3D, + ValueRotation, +} from '../commonTypes'; export type SensorConfig = { interval: number | 'auto'; diff --git a/src/reanimated2/js-reanimated/JSReanimated.ts b/src/reanimated2/js-reanimated/JSReanimated.ts index 04ee23bf217..c736c96cfa1 100644 --- a/src/reanimated2/js-reanimated/JSReanimated.ts +++ b/src/reanimated2/js-reanimated/JSReanimated.ts @@ -1,8 +1,17 @@ import { NativeReanimated } from '../NativeReanimated/NativeReanimated'; -import { ShareableRef } from '../commonTypes'; +import { + SensorType, + ShareableRef, + Value3D, + ValueRotation, +} from '../commonTypes'; import { isJest } from '../PlatformChecker'; +import { WebSensor } from './WebSensor'; export default class JSReanimated extends NativeReanimated { + nextSensorId = 0; + sensors = new Map(); + constructor() { super(false); if (isJest()) { @@ -52,13 +61,48 @@ export default class JSReanimated extends NativeReanimated { ); } - registerSensor(): number { - console.warn('[Reanimated] useAnimatedSensor is not available on web yet.'); - return -1; + registerSensor( + sensorType: SensorType, + interval: number, + eventHandler: (data: Value3D | ValueRotation) => void + ): number { + if (!(this.getSensorName(sensorType) in window)) { + return -1; + } + + const sensor: WebSensor = this.initializeSensor(sensorType, interval); + sensor.addEventListener('reading', () => { + if (sensorType === SensorType.ROTATION) { + const [qw, qx, qy, qz] = sensor.quaternion; + + // reference: https://stackoverflow.com/questions/5782658/extracting-yaw-from-a-quaternion + const yaw = Math.atan2( + 2.0 * (qy * qz + qw * qx), + qw * qw - qx * qx - qy * qy + qz * qz + ); + const pitch = Math.sin(-2.0 * (qx * qz - qw * qy)); + const roll = Math.atan2( + 2.0 * (qx * qy + qw * qz), + qw * qw + qx * qx - qy * qy - qz * qz + ); + eventHandler({ qw, qx, qy, qz, yaw, pitch, roll }); + } else { + const { x, y, z } = sensor; + eventHandler({ x, y, z }); + } + }); + sensor.start(); + + this.sensors.set(this.nextSensorId, sensor); + return this.nextSensorId++; } - unregisterSensor(): void { - // noop + unregisterSensor(id: number): void { + const sensor: WebSensor | undefined = this.sensors.get(id); + if (sensor !== undefined) { + sensor.stop(); + this.sensors.delete(id); + } } subscribeForKeyboardEvents(_: ShareableRef): number { @@ -71,4 +115,38 @@ export default class JSReanimated extends NativeReanimated { unsubscribeFromKeyboardEvents(_: number): void { // noop } + + initializeSensor(sensorType: SensorType, interval: number): WebSensor { + const config = + interval === -1 + ? { referenceFrame: 'device' } + : { frequency: 1 / interval }; + switch (sensorType) { + case SensorType.ACCELEROMETER: + return new window.Accelerometer(config); + case SensorType.GYROSCOPE: + return new window.Gyroscope(config); + case SensorType.GRAVITY: + return new window.GravitySensor(config); + case SensorType.MAGNETIC_FIELD: + return new window.Magnetometer(config); + case SensorType.ROTATION: + return new window.AbsoluteOrientationSensor(config); + } + } + + getSensorName(sensorType: SensorType): string { + switch (sensorType) { + case SensorType.ACCELEROMETER: + return 'Accelerometer'; + case SensorType.GRAVITY: + return 'GravitySensor'; + case SensorType.GYROSCOPE: + return 'Gyroscope'; + case SensorType.MAGNETIC_FIELD: + return 'Magnetometer'; + case SensorType.ROTATION: + return 'AbsoluteOrientationSensor'; + } + } } diff --git a/src/reanimated2/js-reanimated/WebSensor.ts b/src/reanimated2/js-reanimated/WebSensor.ts new file mode 100644 index 00000000000..04d5880550e --- /dev/null +++ b/src/reanimated2/js-reanimated/WebSensor.ts @@ -0,0 +1,34 @@ +export declare class WebSensor { + start: () => void; + stop: () => void; + addEventListener: (eventType: string, eventHandler: () => void) => void; + quaternion: [number, number, number, number]; + x: number; + y: number; + z: number; +} + +type configOptions = + | { + referenceFrame: string; + frequency?: undefined; + } + | { + frequency: number; + referenceFrame?: undefined; + }; + +interface Constructable { + new (config: configOptions): T; +} + +declare global { + interface Window { + Accelerometer: Constructable; + GravitySensor: Constructable; + Gyroscope: Constructable; + Magnetometer: Constructable; + AbsoluteOrientationSensor: Constructable; + Sensor: Constructable; + } +}