Skip to content

Chapitre 7 : API Capacitor Motion - Detection de mouvement

Le plugin @capacitor/motion donne accès aux capteurs de mouvement (accéléromètre, gyroscope). Nous l'utilisons pour détecter quand l'utilisateur "swingue" le telephone comme un sabre.

Créez le fichier src/composables/useMotionDetector.ts :

ts
import { ref } from 'vue';
import { Motion } from '@capacitor/motion';

// Seuil de detection : delta de magnitude en m/s² entre deux lectures
// Une valeur plus basse = plus sensible, plus haute = moins sensible
const SWING_THRESHOLD = 8;

// Cooldown entre deux detections pour eviter les rafales
const COOLDOWN_MS = 300;

/**
 * Composable de detection de swing.
 * @param onSwing - Callback appele quand un swing est detecte.
 *                  Recoit l'intensite du mouvement (delta normalise entre 0 et 1).
 */
export function useMotionDetector(onSwing: (intensity: number) => void) {
  // Etat reactif : est-ce qu'on ecoute l'accelerometre ?
  const listening = ref(false);

  // Variables internes pour l'algorithme de detection
  let lastMag = 0;        // Derniere magnitude mesuree
  let lastSwingTime = 0;  // Timestamp du dernier swing detecte
  let listenerHandle: Awaited<ReturnType<typeof Motion.addListener>> | null = null;

  /**
   * Demande la permission d'acces au capteur de mouvement.
   * Sur iOS 13+, le navigateur exige une demande explicite via
   * DeviceMotionEvent.requestPermission(). Sur Android et desktop,
   * la permission est accordee automatiquement.
   */
  async function requestPermission(): Promise<boolean> {
    if (
      typeof (DeviceMotionEvent as any).requestPermission === 'function'
    ) {
      try {
        const result = await (DeviceMotionEvent as any).requestPermission();
        return result === 'granted';
      } catch {
        return false;
      }
    }
    // Android et desktop : permission implicite
    return true;
  }

  /**
   * Demarre l'ecoute de l'accelerometre.
   *
   * Algorithme de detection :
   * 1. A chaque lecture, on calcule la magnitude du vecteur acceleration
   *    (norme euclidienne de x, y, z)
   * 2. On calcule le delta (difference) avec la magnitude precedente
   * 3. Si le delta depasse le seuil ET que le cooldown est ecoule,
   *    on considere que c'est un swing
   *
   * On utilise accelerationIncludingGravity car c'est la valeur la plus
   * universellement disponible sur tous les appareils.
   */
  async function start() {
    if (listening.value) return;

    const granted = await requestPermission();
    if (!granted) return;

    // Reset des variables
    lastMag = 0;
    lastSwingTime = 0;

    // Ecoute de l'evenement 'accel' du plugin Motion
    listenerHandle = await Motion.addListener('accel', (event) => {
      const a = event.accelerationIncludingGravity;
      if (!a) return;

      // Magnitude = racine carree de (x² + y² + z²)
      const mag = Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z);

      // Delta entre la magnitude actuelle et la precedente
      const delta = Math.abs(mag - lastMag);
      lastMag = mag;

      const now = Date.now();
      // Detection : delta > seuil ET cooldown respecte
      if (delta > SWING_THRESHOLD && now - lastSwingTime > COOLDOWN_MS) {
        lastSwingTime = now;
        // Intensite normalisee entre 0 et 1 (8 = leger, 30+ = tres fort)
        const intensity = Math.min((delta - SWING_THRESHOLD) / 22, 1);
        onSwing(intensity); // Appel du callback avec l'intensite
      }
    });

    listening.value = true;
  }

  /**
   * Arrete l'ecoute de l'accelerometre et libere le listener.
   */
  async function stop() {
    if (listenerHandle) {
      await listenerHandle.remove();
      listenerHandle = null;
    }
    listening.value = false;
  }

  return { listening, start, stop };
}

Points importants :

  • Le pattern Capacitor Listener : Motion.addListener() retourne un handle qu'il faut conserver pour pouvoir appeler .remove() plus tard. C'est un pattern commun a tous les plugins Capacitor bases sur des événements.
  • La permission iOS : depuis iOS 13, Apple exige que l'accès aux capteurs de mouvement soit demande explicitement. C'est pourquoi on appelle DeviceMotionEvent.requestPermission() si cette methode existe.
  • L'algorithme de detection est volontairement simple (delta de magnitude). Un algorithme plus avance pourrait analyser les axes individuellement ou utiliser le gyroscope.
  • Le cooldown empêche de détecter plusieurs swings en rafale quand l'utilisateur secoue le telephone.