one
Version:
One is a React framework for web, iOS, and Android apps.
129 lines (126 loc) • 4.12 kB
JavaScript
import { validateListener } from "./validate.mjs";
const emptyAvailability = {
accelerometer: false,
gyroscope: false,
magnetometer: false,
deviceMotion: false
};
const availability = { ...emptyAvailability };
const listeners = /* @__PURE__ */ new Set();
let observing = false;
let attitude;
function vector(x, y, z, scale = 1) {
return [
x,
y,
z
].every((value) => typeof value === "number" && Number.isFinite(value)) ? {
x: x * scale,
y: y * scale,
z: z * scale
} : void 0;
}
function receive(event) {
const total = event.accelerationIncludingGravity, linear = event.acceleration, rate = event.rotationRate;
const acceleration = vector(total?.x, total?.y, total?.z, -1 / 9.80665);
const user = vector(linear?.x, linear?.y, linear?.z, -1 / 9.80665);
const rotation = vector(rate?.beta, rate?.gamma, rate?.alpha, Math.PI / 180);
availability.accelerometer = !!acceleration;
availability.gyroscope = !!rotation;
availability.deviceMotion = !!user;
const gravity = acceleration && user ? {
x: acceleration.x - user.x,
y: acceleration.y - user.y,
z: acceleration.z - user.z
} : void 0;
for (const listener of [...listeners]) {
if (!listeners.has(listener)) continue;
const value = listener.sensor === "accelerometer" ? acceleration : listener.sensor === "gyroscope" ? rotation : listener.sensor === "deviceMotion" ? user : void 0;
if (!value || event.timeStamp - listener.last < listener.intervalMs) continue;
listener.last = event.timeStamp;
const reading = {
sensor: listener.sensor,
timestampMs: performance.timeOrigin + event.timeStamp,
value
};
if (listener.sensor === "deviceMotion") Object.assign(reading, {
gravity,
userAcceleration: user,
rotationRate: rotation,
attitude
});
listener.onReading(reading);
}
if (!listeners.size) stopObserving();
}
function orient(event) {
const a = event.alpha, b = event.beta, c = event.gamma;
if (!vector(a, b, c)) {
attitude = void 0;
return;
}
const z = a * Math.PI / 180, x = b * Math.PI / 180, y = c * Math.PI / 180;
attitude = {
x: Math.atan2(Math.sin(x), Math.cos(x) * Math.cos(y)),
y: Math.asin(Math.max(-1, Math.min(1, Math.cos(x) * Math.sin(y)))),
z: Math.atan2(Math.sin(z) * Math.cos(y) + Math.cos(z) * Math.sin(x) * Math.sin(y), Math.cos(z) * Math.cos(y) - Math.sin(z) * Math.sin(x) * Math.sin(y))
};
}
function stopObserving() {
if (!observing) return;
window.removeEventListener("devicemotion", receive);
window.removeEventListener("deviceorientation", orient);
observing = false;
attitude = void 0;
}
function observe() {
if (observing || typeof window === "undefined") return;
observing = true;
window.addEventListener("devicemotion", receive);
window.addEventListener("deviceorientation", orient);
}
const Motion = Object.freeze({
getAvailability: () => {
if (typeof window === "undefined" || !window.DeviceMotionEvent) return { ...emptyAvailability };
observe();
return { ...availability };
},
addListener: (sensor, intervalMs, onReading, onError) => {
validateListener(sensor, intervalMs, onReading, onError);
if (typeof window === "undefined") return () => {};
if (sensor === "magnetometer" || !window.DeviceMotionEvent) {
onError("E_MOTION_UNAVAILABLE", "Motion.addListener: sensor is unavailable");
return () => {};
}
observe();
const listener = {
sensor,
intervalMs,
last: -Infinity,
onReading,
onError
};
listeners.add(listener);
const constructor = window.DeviceMotionEvent;
if (constructor.requestPermission) {
const fail = (message) => {
if (!listeners.delete(listener)) return;
if (!listeners.size) stopObserving();
onError("E_MOTION_PERMISSION", message);
};
try {
constructor.requestPermission().then((permission) => {
if (permission !== "granted") fail("Motion.addListener: motion permission is required");
}).catch((error) => fail(String(error)));
} catch (error) {
fail(String(error));
}
}
return () => {
listeners.delete(listener);
if (!listeners.size) stopObserving();
};
}
});
export { Motion };
//# sourceMappingURL=index.mjs.map