You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13移动端震动检测计数器功能实现报错求助

解决方案:Next.js 13 实现移动端震动计数功能

1. 解决SSR环境下的window对象与类型问题

Next.js 13 App Router默认是服务器端渲染,服务器端不存在window对象,且TypeScript默认类型定义中没有requestPermission方法,需要做以下处理:

  • 在组件顶部添加'use client'指令,确保代码仅在客户端执行;
  • 扩展全局类型,避免类型报错。

2. 兼容性处理权限请求

DeviceMotionEvent.requestPermission()是Safari(iOS/macOS)特有的API,其他主流浏览器(Chrome、Firefox等)无需主动请求权限,需做分支判断:

// 扩展全局类型,消除TypeScript报错
declare global {
  interface DeviceMotionEvent {
    requestPermission?: () => Promise<'granted' | 'denied'>;
  }
}

async function requestMotionPermission() {
  if (typeof window !== 'undefined' && 'requestPermission' in DeviceMotionEvent) {
    const permission = await (DeviceMotionEvent.requestPermission as () => Promise<'granted' | 'denied'>)();
    return permission === 'granted';
  }
  // 非Safari环境默认返回已授权
  return true;
}

3. 监听震动并实现计数逻辑

devicemotion事件会持续触发,需要设置加速度阈值判断“有效震动”,同时添加防抖避免重复计数:

'use client';
import { useState, useEffect } from 'react';

declare global {
  interface DeviceMotionEvent {
    requestPermission?: () => Promise<'granted' | 'denied'>;
  }
}

export default function MotionCounter() {
  const [count, setCount] = useState(0);
  let isShaking = false;
  const SHAKE_THRESHOLD = 15; // 可根据设备灵敏度调整阈值

  useEffect(() => {
    async function setupMotionListener() {
      const hasPermission = await requestMotionPermission();
      if (!hasPermission) return;

      function handleMotion(event: DeviceMotionEvent) {
        const acceleration = event.accelerationIncludingGravity;
        if (!acceleration) return;

        // 计算三维合加速度大小
        const totalAcceleration = Math.sqrt(
          Math.pow(acceleration.x || 0, 2) +
          Math.pow(acceleration.y || 0, 2) +
          Math.pow(acceleration.z || 0, 2)
        );

        // 防抖逻辑:避免短时间内重复计数
        if (totalAcceleration > SHAKE_THRESHOLD && !isShaking) {
          isShaking = true;
          setCount(prev => prev + 1);
          setTimeout(() => {
            isShaking = false;
          }, 1000);
        }
      }

      window.addEventListener('devicemotion', handleMotion);
      return () => window.removeEventListener('devicemotion', handleMotion);
    }

    setupMotionListener();
  }, []);

  async function requestMotionPermission() {
    if (typeof window !== 'undefined' && 'requestPermission' in DeviceMotionEvent) {
      const permission = await (DeviceMotionEvent.requestPermission as () => Promise<'granted' | 'denied'>)();
      return permission === 'granted';
    }
    return true;
  }

  return (
    <div>
      <h2>震动次数:{count}</h2>
    </div>
  );
}

关键注意事项

  • Safari要求权限请求必须由用户主动交互触发(比如按钮点击),直接在组件挂载时请求会被拒绝;
  • SHAKE_THRESHOLD值需根据测试调整,数值越大需要震动强度越高才会触发计数;
  • 必须在真实移动端设备上测试,模拟器无法准确模拟设备震动效果。

内容的提问来源于stack exchange,提问作者Ronald Ng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 03:35:25