Skip to content

Instantly share code, notes, and snippets.

@jonastreub
Created May 12, 2019 12:13
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonastreub/c38c146aae5a249207b9de9c33228206 to your computer and use it in GitHub Desktop.
Save jonastreub/c38c146aae5a249207b9de9c33228206 to your computer and use it in GitHub Desktop.
A hook making it fun to work with the gyroscope
import { useState, useEffect } from "react"
interface WorldOrientation {
/** A number representing the motion of the device around the z axis, expressed in degrees with values ranging from 0 to 360. */
alpha: number
/** A number representing the motion of the device around the x axis, expressed in degrees with values ranging from -180 to 180. This represents a front to back motion of the device. */
beta: number
/** A number representing the motion of the device around the y axis, expressed in degrees with values ranging from -90 to 90. This represents a left to right motion of the device. */
gamma: number
}
interface DeviceOrientation {
/** A number representing the horizontal heading, expressed in degrees with values ranging from 0 to 360. */
heading: number
/** A number representing the vertical elevation, expressed in values ranging from 90 degrees up to -90 degrees down. */
elevation: number
/** A number representing tilt, expressed in values ranging from -180 degrees counter clockwise to 180 degrees clockwise. */
tilt: number
}
interface GyroData extends WorldOrientation, DeviceOrientation {
/** A boolean representing whether the device supports gyro events. */
supportsGyro: boolean
}
/** A hook making it fun to work with the gyroscope.
* @returns {@link GyroData}
*/
export function useGyro(): GyroData {
const [data, setData] = useState(initialData)
useEffect(() => {
if (!data.supportsGyro) return
function onOrientationChange(event: DeviceOrientationEvent) {
const { alpha, beta, gamma } = event
// Math only works for non zero values
if (alpha === 0 || beta === 0 || gamma === 0) return
const { heading, elevation, tilt } = worldToDeviceOrientation(event)
setData(previousData => {
return {
...previousData,
alpha,
beta,
gamma,
heading,
elevation,
tilt,
}
})
}
window.addEventListener("deviceorientation", onOrientationChange)
return () => {
window.removeEventListener("deviceorientation", onOrientationChange)
}
}, [])
return data
}
const initialData: GyroData = {
heading: 0,
elevation: 0,
tilt: 0,
alpha: 0,
beta: 0,
gamma: 0,
supportsGyro: hasDeviceOrientationEvent(),
}
function worldToDeviceOrientation(
worldOrientation: WorldOrientation
): DeviceOrientation {
const { alpha, beta, gamma } = worldOrientation
const alphaRad = degToRad(alpha)
const betaRad = degToRad(beta)
const gammaRad = degToRad(gamma)
// Calculate equation components
const cA = Math.cos(alphaRad)
const sA = Math.sin(alphaRad)
const cB = Math.cos(betaRad)
const sB = Math.sin(betaRad)
const cG = Math.cos(gammaRad)
const sG = Math.sin(gammaRad)
// x unitvector
const xrA = -sA * sB * sG + cA * cG
const xrB = cA * sB * sG + sA * cG
const xrC = cB * sG
// y unitvector
const yrA = -sA * cB
const yrB = cA * cB
const yrC = -sB
// -z unitvector
const zrA = -sA * sB * cG - cA * sG
const zrB = cA * sB * cG - sA * sG
const zrC = cB * cG
// Calculate heading
let heading = Math.atan(zrA / zrB)
// Convert from half unit circle to whole unit circle
if (zrB < 0) {
heading += Math.PI
} else if (zrA < 0) {
heading += 2 * Math.PI
}
// Calculate elevation
const elevation = Math.PI / 2 - Math.acos(-zrC)
// Calculate tilt
const cH = Math.sqrt(1 - zrC * zrC)
const tilt = Math.acos(-xrC / cH) * Math.sign(yrC)
return {
heading: radToDeg(heading),
elevation: radToDeg(elevation),
tilt: radToDeg(tilt),
}
}
function degToRad(deg) {
return (deg * Math.PI) / 180
}
function radToDeg(rad) {
return (rad * 180) / Math.PI
}
function hasDeviceOrientationEvent() {
return !!window["DeviceOrientationEvent"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment