欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

📌 开发环境声明:本文基于 React Native 0.72.90 版本进行开发适配


🚀 一、开篇引言

摇一摇是移动应用中常见的交互方式,广泛应用于摇一摇抽奖、摇一摇换题、摇一摇反馈等场景。react-native-shake 是 React Native 社区中简单易用的摇一摇事件监听组件,可以检测用户摇晃手机的动作并触发相应事件。本文将带你深入了解如何在 HarmonyOS 平台上集成和使用这个有趣的交互组件。

1.1 大纲

  • ✅ react-native-shake 的核心概念与工作原理
  • ✅ HarmonyOS 平台的完整集成流程
  • ✅ 摇一摇事件监听与处理
  • ✅ API 的深度解析
  • ✅ 实际应用场景的最佳实践

1.2 适用人群

  • 正在进行 React Native 鸿蒙化迁移的开发者
  • 需要实现摇一摇功能的开发者
  • 对跨平台传感器组件开发感兴趣的技术爱好者

1.3 为什么选择 react-native-shake?

特点 说明
简单易用 API 极简,几行代码即可实现
跨平台一致 iOS、Android、HarmonyOS 表现一致
轻量级 无需复杂配置,开箱即用
事件驱动 基于订阅模式,灵活管理监听生命周期
低功耗 智能检测,不影响设备续航

📦 二、库概览

2.1 基本信息

项目 内容
库名称 @react-native-ohos/react-native-shake
原库名称 react-native-shake
版本信息 5.6.3 (RN 0.72) / 6.0.2 (RN 0.77)
官方仓库 https://github.com/Doko-Demo-Doa/react-native-shake
鸿蒙仓库 https://gitcode.com/openharmony-sig/rntpc_react-native-shake
开源协议 MIT

2.2 版本兼容性

三方库版本 支持RN版本 是否支持Autolink
~6.0.2 0.77 No
~5.6.3 0.72 Yes
<=5.6.2-0.0.1@deprecated 0.72 No

2.3 核心能力矩阵

能力项 描述 HarmonyOS 支持
添加监听 addListener 方法 ✅ 完全支持
移除监听 remove 方法 ✅ 完全支持
移除所有监听 removeAllListeners 方法 ✅ 完全支持

2.4 技术架构图

原生平台层

Bridge Layer

React Native 应用层

RNShake Module

addListener

remove

removeAllListeners

Native Module

ShakePackage

ShakeModule

Android
SensorManager

iOS
CMMotionManager

HarmonyOS
Accelerometer

2.5 典型应用场景

场景 描述 示例
摇一摇抽奖 摇动触发抽奖活动 🎰 营销活动、福利发放
摇一摇换题 摇动切换题目或内容 📝 题库应用、随机推荐
摇一摇反馈 摇动触发反馈入口 💬 用户反馈、问题报告
摇一摇刷新 摇动刷新页面内容 🔄 内容刷新、数据更新
摇一摇彩蛋 隐藏功能触发 🥚 彩蛋功能、隐藏菜单

⚡ 三、快速开始

3.1 环境要求

依赖项 版本要求
React Native 0.72.x / 0.77.x
RNOH (鸿蒙框架) 0.72.90 / 0.77.18
HarmonyOS SDK 6.0.0.47+ (API 20)
DevEco Studio 5.0.3+ / 6.0+
Node.js 16.18.0+ / 18.x

3.2 一键安装

创建鸿蒙项目的过程不再进行描述,不懂得看这篇:https://blog.csdn.net/u011178696/article/details/151932277
在这里插入图片描述

npm install @react-native-ohos/react-native-shake@5.6.3-rc.1

或使用 yarn:

yarn add @react-native-ohos/react-native-shake@5.6.3-rc.1

3.3 验证安装

安装完成后,检查 package.json 文件:

{
  "dependencies": {
    "@react-native-ohos/react-native-shake": "^5.6.3-rc.1"
  }
}

🔧 四、HarmonyOS 平台配置

4.1 权限配置

在这里插入图片描述

⚠️ 此库需要加速度计权限,必须在 module.json5 中配置。

打开 entry/src/main/module.json5,添加权限:

"requestPermissions": [
  {
    "name": "ohos.permission.ACCELEROMETER"
  }
]

4.2 AutoLink 配置(推荐)

🎉 5.6.3 版本支持 AutoLink,可自动完成原生端配置!

如果你的项目已接入 AutoLink,安装依赖后会自动完成原生端配置,无需手动操作。

Autolink 框架指导文档:https://gitcode.com/openharmony-sig/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md

4.3 ManualLink 配置(手动配置)

⚠️ 如果你使用的是 6.0.2 或更高版本,需要手动配置原生端代码。

步骤 1:配置 oh-package.json5

在项目根目录的 oh-package.json5 文件中添加(根据自己的版本来,不一定是我这个版本):
在这里插入图片描述

{
  "overrides": {
    "@rnoh/react-native-openharmony": "0.72.90"
  }
}
步骤 2:引入 HAR 包

entry/oh-package.json5 文件中添加依赖:

"dependencies": {
  "@rnoh/react-native-openharmony": "0.72.90",
  "@react-native-ohos/react-native-shake": "file:../../node_modules/@react-native-ohos/react-native-shake/harmony/shake_package.har"
}
步骤 3:配置 CMakeLists.txt

entry/src/main/cpp/CMakeLists.txt 文件中添加:

+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")

+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-shake/src/main/cpp" ./shake_package)

+ target_link_libraries(rnoh_app PUBLIC rnoh_shake)
步骤 4:配置 PackageProvider.cpp

entry/src/main/cpp/PackageProvider.cpp 文件中添加:

+ #include "ShakePackage.h"

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
+     std::make_shared<ShakePackage>(ctx)
    };
}
步骤 5:引入 ShakePackage

entry/src/main/ets/RNPackagesFactory.ts 文件中添加:

+ import { ShakePackage } from "@react-native-ohos/react-native-shake/ts";

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
+   new ShakePackage(ctx)
  ];
}

📱 五、基础使用

5.1 添加摇一摇监听

最基础的使用方式:

import RNShake from 'react-native-shake';

useEffect(() => {
  const subscription = RNShake.addListener(() => {
    console.log('检测到摇一摇!');
  });

  return () => {
    subscription.remove();
  };
}, []);

5.2 完整示例

import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import RNShake from 'react-native-shake';

export default function App() {
  const [shakeCount, setShakeCount] = useState(0);

  useEffect(() => {
    const subscription = RNShake.addListener(() => {
      setShakeCount((prev) => prev + 1);
    });

    return () => {
      subscription.remove();
    };
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>摇一摇计数器</Text>
      <Text style={styles.count}>{shakeCount}</Text>
      <Text style={styles.hint}>摇晃手机试试!</Text>
    </View>
  );
}

🎨 六、进阶用法

6.1 摇一摇触发动画

结合动画效果增强用户体验:

import { Animated } from 'react-native';

const [scale] = useState(new Animated.Value(1));

useEffect(() => {
  const subscription = RNShake.addListener(() => {
    Animated.sequence([
      Animated.timing(scale, {
        toValue: 1.2,
        duration: 100,
        useNativeDriver: true,
      }),
      Animated.timing(scale, {
        toValue: 1,
        duration: 100,
        useNativeDriver: true,
      }),
    ]).start();
  });

  return () => subscription.remove();
}, []);

6.2 摇一摇触发随机内容

const items = ['🍎', '🍊', '🍋', '🍇', '🍓', '🍑'];
const [currentItem, setCurrentItem] = useState('🍎');

useEffect(() => {
  const subscription = RNShake.addListener(() => {
    const randomIndex = Math.floor(Math.random() * items.length);
    setCurrentItem(items[randomIndex]);
  });

  return () => subscription.remove();
}, []);

6.3 移除所有监听

RNShake.removeAllListeners();

📚 七、API 详解

7.1 RNShake 方法

addListener

添加摇一摇事件监听器,返回一个订阅对象。

const subscription = RNShake.addListener(() => {
  console.log('摇一摇触发!');
});

subscription.remove();
removeAllListeners

移除所有摇一摇事件监听器。

RNShake.removeAllListeners();

⚠️ 八、注意事项与常见问题

8.1 权限要求

权限名称 说明
ohos.permission.ACCELEROMETER 加速度计权限

8.2 常见问题

Q1: 摇一摇不触发?

A: 检查是否已配置 ohos.permission.ACCELEROMETER 权限。

Q2: 如何调整灵敏度?

A: 当前版本使用默认灵敏度,暂不支持自定义灵敏度配置。

Q3: 监听器没有正确移除?

A: 确保在 useEffect 的清理函数中调用 subscription.remove()

Q4: 摇晃时弹出 React Native Dev Menu?

A: 这是 React Native 开发模式的默认行为。在开发环境中,摇晃设备会触发开发者菜单。有以下解决方案:

  1. 生产环境自动禁用:在 release 构建中,开发者菜单会自动禁用,不会影响用户体验。

  2. 开发环境临时禁用:如果需要在开发环境测试摇一摇功能,可以在 Index.ets 中修改 rnInstanceConfig 配置,设置 devMenuEnabled: false

  3. 调整摇晃灵敏度:可以适当降低 react-native-shake 的触发频率,避免与系统开发者菜单冲突。


💻 九、完整示例代码

精美摇一摇示例

在这里插入图片描述

import React, { useState, useEffect, useRef } from 'react';
import {
  View,
  Text,
  StyleSheet,
  SafeAreaView,
  Animated,
  TouchableOpacity,
} from 'react-native';
import RNShake from 'react-native-shake';

const emojis = ['🎲', '🎰', '🎯', '🎪', '🎨', '🎭'];
const messages = [
  '运气不错!',
  '再来一次!',
  '好运连连!',
  '惊喜不断!',
];

export default function App() {
  const [shakeCount, setShakeCount] = useState(0);
  const [currentEmoji, setCurrentEmoji] = useState('🎲');
  const [message, setMessage] = useState('摇晃手机试试');
  const [isShaking, setIsShaking] = useState(false);

  const scaleAnim = useRef(new Animated.Value(1)).current;
  const rotateAnim = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    const subscription = RNShake.addListener(() => {
      handleShake();
    });

    return () => {
      subscription.remove();
    };
  }, []);

  const handleShake = () => {
    if (isShaking) return;

    setIsShaking(true);
    setShakeCount((prev) => prev + 1);

    const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)];
    const randomMessage = messages[Math.floor(Math.random() * messages.length)];
    setCurrentEmoji(randomEmoji);
    setMessage(randomMessage);

    Animated.sequence([
      Animated.parallel([
        Animated.timing(scaleAnim, {
          toValue: 1.3,
          duration: 150,
          useNativeDriver: true,
        }),
        Animated.timing(rotateAnim, {
          toValue: 1,
          duration: 150,
          useNativeDriver: true,
        }),
      ]),
      Animated.parallel([
        Animated.timing(scaleAnim, {
          toValue: 1,
          duration: 150,
          useNativeDriver: true,
        }),
        Animated.timing(rotateAnim, {
          toValue: 0,
          duration: 150,
          useNativeDriver: true,
        }),
      ]),
    ]).start(() => {
      setIsShaking(false);
    });
  };

  const resetCount = () => {
    setShakeCount(0);
    setCurrentEmoji('🎲');
    setMessage('摇晃手机试试');
  };

  const spin = rotateAnim.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '360deg'],
  });

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>摇一摇</Text>
      </View>

      <View style={styles.content}>
        <Animated.View
          style={[
            styles.emojiContainer,
            {
              transform: [{ scale: scaleAnim }, { rotate: spin }],
            },
          ]}
        >
          <Text style={styles.emoji}>{currentEmoji}</Text>
        </Animated.View>

        <Text style={styles.message}>{message}</Text>

        <View style={styles.statsContainer}>
          <View style={styles.statItem}>
            <Text style={styles.statValue}>{shakeCount}</Text>
            <Text style={styles.statLabel}>摇动次数</Text>
          </View>
        </View>
      </View>

      <View style={styles.footer}>
        <TouchableOpacity style={styles.resetButton} onPress={resetCount}>
          <Text style={styles.resetButtonText}>重置计数</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#1a1a2e',
  },
  header: {
    padding: 20,
    backgroundColor: '#16213e',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
    textAlign: 'center',
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingHorizontal: 20,
  },
  emojiContainer: {
    width: 140,
    height: 140,
    borderRadius: 70,
    backgroundColor: '#16213e',
    justifyContent: 'center',
    alignItems: 'center',
    marginBottom: 30,
    borderWidth: 3,
    borderColor: '#00d4ff',
    shadowColor: '#00d4ff',
    shadowOffset: { width: 0, height: 0 },
    shadowOpacity: 0.5,
    shadowRadius: 20,
    elevation: 10,
  },
  emoji: {
    fontSize: 64,
  },
  message: {
    fontSize: 22,
    fontWeight: '600',
    color: '#00d4ff',
    marginBottom: 40,
  },
  statsContainer: {
    flexDirection: 'row',
  },
  statItem: {
    alignItems: 'center',
    backgroundColor: '#16213e',
    paddingHorizontal: 40,
    paddingVertical: 20,
    borderRadius: 20,
  },
  statValue: {
    fontSize: 40,
    fontWeight: 'bold',
    color: '#fff',
  },
  statLabel: {
    fontSize: 14,
    color: '#888',
    marginTop: 5,
  },
  footer: {
    padding: 20,
  },
  resetButton: {
    backgroundColor: '#00d4ff',
    paddingVertical: 14,
    borderRadius: 12,
    alignItems: 'center',
  },
  resetButtonText: {
    fontSize: 16,
    color: '#1a1a2e',
    fontWeight: '600',
  },
});

🔗 十、相关资源


📝 十一、总结

本文详细介绍了 react-native-shake 在 HarmonyOS 平台的使用方法。通过 Shake 组件,你可以轻松实现摇一摇交互功能。

核心要点

  • ✅ 简单易用的 API 设计
  • ✅ 基于订阅模式的事件监听
  • ✅ 需要配置加速度计权限
  • ✅ 记得在组件卸载时移除监听

希望本文能帮助你在 HarmonyOS 项目中顺利集成摇一摇功能!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐