motion.mjs
3import { signal } from '../core/signal.mjs';
The accelerometer's x-axis value ranges from 0 to 1. @name accelerationX @return {Pattern} @synonyms accX @tags external_io @example n(accelerationX.segment(4).range(0,7)).scale("C:minor")
The accelerometer's y-axis value ranges from 0 to 1. @name accelerationY @return {Pattern} @synonyms accY @tags external_io @example n(accelerationY.segment(4).range(0,7)).scale("C:minor")
The accelerometer's z-axis value ranges from 0 to 1. @name accelerationZ @return {Pattern} @synonyms accZ @tags external_io @example n(accelerationZ.segment(4).range(0,7)).scale("C:minor")
The device's gravity x-axis value ranges from 0 to 1. @name gravityX @return {Pattern} @synonyms gravX @tags external_io @example n(gravityX.segment(4).range(0,7)).scale("C:minor")
The device's gravity y-axis value ranges from 0 to 1. @name gravityY @return {Pattern} @synonyms gravY @tags external_io @example n(gravityY.segment(4).range(0,7)).scale("C:minor")
The device's gravity z-axis value ranges from 0 to 1. @name gravityZ @return {Pattern} @synonyms gravZ @tags external_io @example n(gravityZ.segment(4).range(0,7)).scale("C:minor")
The device's rotation around the alpha-axis value ranges from 0 to 1. @name rotationAlpha @return {Pattern} @synonyms rotA, rotZ, rotationZ @tags external_io @example n(rotationAlpha.segment(4).range(0,7)).scale("C:minor")
The device's rotation around the beta-axis value ranges from 0 to 1. @name rotationBeta @return {Pattern} @synonyms rotB, rotX, rotationX @tags external_io @example n(rotationBeta.segment(4).range(0,7)).scale("C:minor")
The device's rotation around the gamma-axis value ranges from 0 to 1. @name rotationGamma @return {Pattern} @synonyms rotG, rotY, rotationY @tags external_io @example n(rotationGamma.segment(4).range(0,7)).scale("C:minor")
The device's orientation alpha value ranges from 0 to 1. @name orientationAlpha @return {Pattern} @synonyms oriA, oriZ, orientationZ @tags external_io @example n(orientationAlpha.segment(4).range(0,7)).scale("C:minor")
The device's orientation beta value ranges from 0 to 1. @name orientationBeta @return {Pattern} @synonyms oriB, oriX, orientationX @tags external_io @example n(orientationBeta.segment(4).range(0,7)).scale("C:minor")
The device's orientation gamma value ranges from 0 to 1. @name orientationGamma @return {Pattern} @synonyms oriG, oriY, orientationY @tags external_io @example n(orientationGamma.segment(4).range(0,7)).scale("C:minor")
The device's absolute orientation alpha value ranges from 0 to 1. @name absoluteOrientationAlpha @return {Pattern} @synonyms absOriA, absOriZ, absoluteOrientationZ @tags external_io @example n(absoluteOrientationAlpha.segment(4).range(0,7)).scale("C:minor")
The device's absolute orientation beta value ranges from 0 to 1. @name absoluteOrientationBeta @return {Pattern} @synonyms absOriB, absOriX, absoluteOrientationX @tags external_io @example n(absoluteOrientationBeta.segment(4).range(0,7)).scale("C:minor")
The device's absolute orientation gamma value ranges from 0 to 1. @name absoluteOrientationGamma @return {Pattern} @synonyms absOriG, absOriY, absoluteOrientationY @tags external_io @example n(absoluteOrientationGamma.segment(4).range(0,7)).scale("C:minor")
181 this._gravity = { 182 x: 0, 183 y: 0, 184 z: 0, 185 }; 186 187 this._rotation = { 188 alpha: 0, 189 beta: 0, 190 gamma: 0, 191 }; 192 193 this._orientation = { 194 alpha: 0, 195 beta: 0, 196 gamma: 0, 197 }; 198 199 this._absoluteOrientation = { 200 alpha: 0, 201 beta: 0, 202 gamma: 0, 203 }; 204 205 this._permissionStatus = 'unknown'; 206 } 207 208 async requestPermissions() { 209 if (typeof DeviceMotionEvent?.requestPermission === 'function') { 210 try { 211 // iOS requires explicit permission 212 const motionPermission = await DeviceMotionEvent.requestPermission(); 213 const orientationPermission = await DeviceOrientationEvent.requestPermission(); 214 215 this._permissionStatus = 216 motionPermission === 'granted' && orientationPermission === 'granted' ? 'granted' : 'denied'; 217 this.setupEventListeners(); 218 } catch (error) { 219 console.error('Permission request failed:', error); 220 this._permissionStatus = 'denied'; 221 } 222 } else { 223 this._permissionStatus = 'granted'; 224 this.setupEventListeners(); 225 } 226 } 227 228 setupEventListeners() { 229 if (this._permissionStatus === 'granted') { 230 // Device Motion handler 231 window.addEventListener('devicemotion', this.handleDeviceMotion.bind(this), true); 232 window.addEventListener('deviceorientation', this.handleDeviceOrientation.bind(this), true); 233 window.addEventListener('deviceorientationabsolute', this.handleAbsoluteDeviceOrientation.bind(this), true); 234 } 235 } 236 237 handleDeviceMotion(event) { 238 //console.log(event); 239 if (event.acceleration) { 240 // Normalize acceleration values to 0-1 range 241 this._acceleration.x = (event.acceleration.x + 1) / 2; 242 this._acceleration.y = (event.acceleration.y + 1) / 2; 243 this._acceleration.z = (event.acceleration.z + 1) / 2; 244 } 245 246 if (event.accelerationIncludingGravity) { 247 // Normalize acceleration values to 0-1 range 248 this._gravity.x = (event.accelerationIncludingGravity.x + this.GRAVITY) / (2 * this.GRAVITY); 249 this._gravity.y = (event.accelerationIncludingGravity.y + this.GRAVITY) / (2 * this.GRAVITY); 250 this._gravity.z = (event.accelerationIncludingGravity.z + this.GRAVITY) / (2 * this.GRAVITY); 251 } 252 253 if (event.rotationRate) { 254 // Normalize rotation values to 0-1 range 255 this._rotation.alpha = (event.rotationRate.alpha + 180) / 360; 256 this._rotation.beta = (event.rotationRate.beta + 180) / 360; 257 this._rotation.gamma = (event.rotationRate.gamma + 180) / 360; 258 } 259 } 260 261 handleDeviceOrientation(event) { 262 this._orientation.alpha = event.alpha / 360; //a(0~360) 263 this._orientation.beta = (event.beta + 180) / 360; //b(-180~180) 264 this._orientation.gamma = (event.gamma + 90) / 180; //g(-90~90) 265 } 266 267 handleAbsoluteDeviceOrientation(event) { 268 this._absoluteOrientation.alpha = event.alpha / 360; //a(0~360) 269 this._absoluteOrientation.beta = (event.beta + 180) / 360; //b(-180~180) 270 this._absoluteOrientation.gamma = (event.gamma + 90) / 180; //g(-90~90) 271 }
Getter methods for current values
274 getAcceleration() { 275 return this._acceleration; 276 } 277 getGravity() { 278 return this._gravity; 279 } 280 getRotation() { 281 return this._rotation; 282 } 283 getOrientation() { 284 return this._orientation; 285 } 286 getAbsoluteOrientation() { 287 return this._absoluteOrientation; 288 } 289}
Create singleton instance
292const deviceMotion = new DeviceMotionHandler();
Export a function to request permission
Create signals for acceleration
Aliases for shorter names
Create signals for gravity
Aliases for shorter names
Create signals for orientation
Aliases for shorter names
Aliases mapping to X,Y,Z coordinates
Short aliases for A,B,G,X,Y,Z
Create signals for absolute orientation
344export const absoluteOrientationAlpha = signal(() => deviceMotion.getAbsoluteOrientation().alpha); 345export const absoluteOrientationBeta = signal(() => deviceMotion.getAbsoluteOrientation().beta); 346export const absoluteOrientationGamma = signal(() => deviceMotion.getAbsoluteOrientation().gamma);
Aliases for shorter names
Aliases mapping to X,Y,Z coordinates
Short aliases for X,Y,Z
Create signals for rotation
364export const rotationAlpha = signal(() => deviceMotion.getRotation().alpha); 365export const rotationBeta = signal(() => deviceMotion.getRotation().beta); 366export const rotationGamma = signal(() => deviceMotion.getRotation().gamma); 367export const rotationX = rotationBeta; 368export const rotationY = rotationGamma; 369export const rotationZ = rotationAlpha;
Aliases for shorter names
// Bipolar versions (ranging from -1 to 1 instead of 0 to 1) export const accX2 = accX.toBipolar(); export const accY2 = accY.toBipolar(); export const accZ2 = accZ.toBipolar();
export const rotA2 = rotA.toBipolar(); export const rotB2 = rotB.toBipolar(); export const rotG2 = rotG.toBipolar();