前端摇一摇功能基本实现

冰岩作坊 September 22, 2022

这里的 摇一摇 指什么

简单来说,就是通过摇动移动设备去完成一些功能

那么对于程序员而言,主要是监听移动设备摇动的事件

我们如何判断用户是否摇动了移动设备,又如何获得摇动的相关信息

DeviceMotionEvent API

由 HTML5 提供,可以获得设备的物理方向及运动信息

这里我们用到的主要是其中 devicemotion 事件,提供设备的加速度信息,有 4 个只读属性:

  1. accelerationIncludingGravity:重力加速度
  2. acceleration:加速度(需要设备陀螺仪支持)
  3. rotationRate(alpha,beat,gamma):旋转速度
  4. interval:获取的时间间隔
    封装事件监听函数
1
const useEventListener = (  event: string,  eventHandler,  useCapture = false) => ;};

监听 devicemotion 事件

1
const unRegister = useEventListener(            "devicemotion",            onDeviceMotionHandler          );

具体实现

由上文可知,我们可以获得的是三维加速度信息,所以首先需要由加速度计算得速度

这里用加速度在 x、y、z 三个方向单位时间变化率来表示速度

1
const calculateSpeed = (  curA: DeviceMotionEvent["acceleration"],  lastA: DeviceMotionEvent["acceleration"],  diff: number) =>  (Math.sqrt(    Math.pow(curA!.x! - lastA!.x!, 2) +      Math.pow(curA!.y! - lastA!.y!, 2) +      Math.pow(curA!.z! - lastA!.z!, 2)  ) /    diff) *  10000;

此时我们可以通过实际测试,设置一个临界速度,当实际速度超过临界速度时,即认为发生了一次摇动

1
const CRITICAL_SPEED = 800;

记录开始时间与开始时间后的摇动次数,则可计算出从开始时间到当前时间的摇动频率

当时间间隔足够短时,可认为获得的是当前时刻的摇动频率

1
// 这三个变量都对外暴露,使用 ref 可以同步修改const frequency = ref(0);const shakeTimes = ref(0);const startTime = ref(Date.now());let lastTime = Date.now(); // 上一次时间let lastA: DeviceMotionEvent["acceleration"] = ; // 上一次加速度const deviceMotionHandler = (e: DeviceMotionEvent) => ;  

至此,我们已经可以判断用户是否摇动了移动设备,且获得摇动次数与摇动频率

兼容各种情况

核心功能的实现并不复杂,但在实际应用中会碰到各种不兼容的情况需要处理

注意我们的网站需要支持 HTTPS 协议,才能使用 DeviceMotionEvent API

在此之前先封装处理 await

1
const awaitWraper = (promise) => promise.then((res) => [null, res]).catch((err) => [err, null]);

接着将之前的具体实现包装成 registerOnDeviceMotion 函数,用于注册 devicemotion 事件

1
interface registerOnDeviceMotionReturnType const registerOnDeviceMotion = async (): Promise => ;    /* 如果设备不支持 devicemotion 事件 */    if (!window.DeviceMotionEvent)     let lastTime = Date.now(); // 上一次时间    let lastA: DeviceMotionEvent["acceleration"] = ; // 上一次加速度    /* 事件处理函数 */    const deviceMotionHandler = (e: DeviceMotionEvent) => ;    /* IOS 13 */    if (typeof DeviceMotionEvent.requestPermission === "function")  else       } else     }    returnObj.success = true;    returnObj.unRegister = useEventListener(      "devicemotion",      onDeviceMotionHandler    );    return returnObj;  };   

在 IOS 中,在使用 DeviceMotionEvent API 时需要用户手动授权

我们可以将用户授权的行为绑定给某个在发生该行为前必须经历的事件,比如点击开始按钮

1
const getPermission = () => )    if (permissionRes.error) alert(permissionRes.error) // 这里用 alert 模拟一下展示错误信息  });}const clickHandler = () => ;

最终在某个 DOM 元素上监听 click 事件

1