返回博客首页
← 所有文章

在 NativeScript 中检测摇晃

2019年12月3日 — 作者:Alexander Vakrilov

在本篇博客中,我们将学习如何使用nativescript-accelerometer插件在 NativeScript 应用中检测“摇晃”手势。

shaking dog

获取加速度计数据

首先,我们将使用nativescript-accelerometer插件开始监听加速度计数据。

import { startAccelerometerUpdates, AccelerometerData } from "nativescript-accelerometer";

startAccelerometerUpdates((data: AccelerometerData) => {
    console.log("x: " + data.x + "y: " + data.y + "z: " + data.z);
}, { sensorDelay: "ui" });

AccelerometerData为我们提供了每个x、y、z轴的当前加速度计读数。因为我们传递了sensorDelay: "ui",所以插件将大约每60毫秒给我们一个读数,这对于我们的目的来说似乎足够了。

phone axis

我们获得的值是标准化的。值1实际上等于地球的加速度(9.81 m/s2)。这意味着如果手机静止,AccelerometerData向量将指向地面,其长度($\sqrt{x^2+y^2+z^2}$)将等于1

注意:这也意味着如果手机处于自由落体状态(或被抛向空中),该向量的长度将为零,因为它将处于失重状态。总之:手机不动 -> 向量长度为1,手机下落 -> 向量长度为零。这是加速度计工作原理的一个有趣(且有点违反直觉)的结果。

检测摇晃

现在,让我们实现摇晃检测。

摇晃手机意味着我们正在沿不同方向对其施加力。我们从牛顿第二定律知道$\vec{F}=m\vec{a}$,这会导致加速度向量发生变化。

我们将使用ShakeDetector实现。它接受一个在检测到摇晃时要调用的callback,并期望其onSensorData(data)被加速度计的值调用。加速度计更新的启动/停止由类的使用者负责。

import { time } from "tns-core-modules/profiling";
import { AccelerometerData } from "nativescript-accelerometer";

const FORCE_THRESHOLD = 0.5;
const TIME_THRESHOLD = 100;
const SHAKE_TIMEOUT = 800;
const SHAKE_THROTTLE = 1000;
const SHAKE_COUNT = 3;

export class ShakeDetector {
  private lastTime = 0;
  private lastShake = 0;
  private lastForce = 0;
  private shakeCount = 0;
  private cb: Function;

  constructor(callback: () => void) {
    this.cb = zonedCallback(callback);
  }

  public onSensorData(data: AccelerometerData) {
    const now = time();
    if ((now - this.lastForce) > SHAKE_TIMEOUT) {
      this.shakeCount = 0;
    }

    const timeDelta = now - this.lastTime;
    if (timeDelta > TIME_THRESHOLD) {
      const forceVector = Math.abs(Math.sqrt(Math.pow(data.x, 2) + Math.pow(data.y, 2) + Math.pow(data.z, 2)) - 1);

      if (forceVector > FORCE_THRESHOLD) {
        this.shakeCount++;
        if ((this.shakeCount >= SHAKE_COUNT) && (now - this.lastShake > SHAKE_THROTTLE)) {
          this.lastShake = now;
          this.shakeCount = 0;

          this.cb();
        }
        this.lastForce = now;
      }

      this.lastTime = now;
    }
  }
}

ShakeDetector将在以下情况下检测到摇晃:

  • 注册3个读数(SHAKE_COUNT
  • 这些读数之间的时间间隔小于800毫秒SHAKE_TIMEOUT
  • 并且这些读数的加速度向量长度与1(我们从地球引力获得的恒定加速度)的差值至少为0.5FORCE_THRESHOLD

如果满足所有这些条件,我们将调用callback并在再次触发事件之前等待1秒(SHAKE_THROTTLE),以避免在长时间摇晃期间多次触发事件。

有趣的是,我们甚至不关心加速度向量方向的变化。这使得该算法略有不准确。如果您还记得上一节中的说明 - 如果手机正在下落,加速度向量将为0,我们的检查将检测为摇晃。但是我发现,在正常使用中,很难长时间持续地沿一个方向对手机施加加速度。您需要改变方向 - 这实际上就是摇晃。因此,对于实际使用来说,它已经足够好了(除非您在太空中👨‍🚀)。

整合在一起

剩下的唯一事情就是实例化一个ShakeDetector并在加速度计更新时通知它。

const shakeDetector = new ShakeDetector(() => {
  alert("Shake detected!!!")
});

startAccelerometerUpdates(
  (data) => shakeDetector.onSensorData(data), 
  { sensorDelay: "ui" }
);

您可以在nativescript-accelerometer仓库中查看完整的演示。