npm package: @hadss/react_native_vibrator
Description: Shared Vibrator with OpenHarmony feature
Latest version: 1.0.0-rc.1

# @hadss/react_native_vibrator

## 介绍

为 React Native 提供振动功能，当设备需要配置不同的振动模式时，可以调用Vibrator模块。该库封装了系统振动的相关能力，开发者可通过简单的接口在React Native中实现振动功能。


## 工程目录

```
.
├─harmony                                             
|    ├─vibrator
│    │  └─src
│    │      ├─main
│    │      │    └─constants
│    │      │        └─TypeConstants.ets           // 类型常量
│    │      │        └─VibratorConstants.ets       // 错误码常量
│    │      │    └─ets
│    │      │      ├─VibratorModule.ets            // Turbo Modules接口ArkTS实现
│    │      │      ├─VibratorModulePackage.ets     // VibratorModuleFactory
│    │      │      └─VibratorModuleSpec.ts         // Turbo Modules接口
│    │      │      └─VibratorType.ts               // 接口参数类型
|    └─build-profile.json5                         // 编译配置文件（多环境Harmony/OpenHarmony）
├─src
│  ├─index.ts                                      // 模块导出的接口
│  └─turbo    
|     └─VibratorModule.ts                          // Turbo Modules接口
```

## 安装与使用

进入到工程目录并输入以下命令：

<!-- tabs:start -->

**npm**

```bash
npm install @hadss/react_native_vibrator
```

**yarn**

```bash
yarn add @hadss/react_native_vibrator
```
下面的代码展示了这个库的基本使用场景：

**Vibrator使用示例** 
```jsx
import { VibratorStopMode, Vibrator } from '@hadss/react_native_vibrator';
import React, { useState } from 'react';
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ColorValue } from 'react-native';
const TAG = 'VibratorTest';
// 查询是否支持传入的振动
const handleSupportEffect = (): void => {
  Vibrator.isSupportEffect('haptic.notice.success')
    .then((state: boolean) => {
      console.log(TAG, `isSupportEffect : ${state}`);
    })
    .catch((err) => {
      console.log(TAG, `handleSupportEffect error : ${JSON.stringify(err)}`);
    });
};

// 按时间振动
const handleStarVibrateTime = (): void => {
  Vibrator.startVibration(
    {
      type: 'time',
      duration: 3000,
    },
    {
      id: 0,
      usage: 'ring',
    },
  )
    .then(() => {
      console.log(TAG, 'handleStarVibrateTime done');
    })
    .catch((err) => {
      console.log(TAG, `handleStarVibrateTime error : ${JSON.stringify(err)}`);
    });
};

// 按文件振动，文件在原生工程rawfile目录下
const handleStartVibrateFromFile = (): void => {
  Vibrator.startVibration(
    {
      type: 'file',
      fileName: 'vibrator/file.json',
    },
    {
      id: 0,
      usage: 'alarm',
    },
  )
    .then(() => {
      console.log(TAG, 'handleStartVibrateFromFile done');
    })
    .catch((err) => {
      console.log(TAG, `handleStartVibrateFromFile error : ${JSON.stringify(err)}`);
    });
};

// 按Preset振动
const handleStartVibratePreset = (): void => {
  Vibrator.startVibration(
    {
      type: 'preset',
      effectId: 'haptic.notice.success',
      intensity: 1,
      count: 15,
    },
    {
      id: 0,
      usage: 'notification',
    },
  )
    .then(() => {
      console.log(TAG, 'handleStartVibratePreset done');
    })
    .catch((err) => {
      console.log(TAG, `handleStartVibratePreset: ${JSON.stringify(err)}`);
    });
};

// 按照指定模式停止振动
const handleStopVibrationByMode = (): void => {
  Vibrator.stopVibration(VibratorStopMode.VIBRATOR_STOP_MODE_TIME)
    .then(() => {
      console.log(TAG, 'handleStopVibrationByMode done');
    })
    .catch((err) => {
      console.log(TAG, `handleStopVibrationByMode error : ${JSON.stringify(err)}`);
    });
};

// 停止所有的振动
const handleStopVibration = (): void => {
  Vibrator.stopVibration()
    .then(() => {
      console.log(TAG, 'handleStopVibration done');
    })
    .catch((err) => {
      console.log(TAG, `handleStopVibration error : ${JSON.stringify(err)}`);
    });
};

// 停止所有的振动
const handleStopVibrationSync = (): void => {
  Vibrator.stopVibrationSync();
};

const dataList = [
  {
    id: 0,
    text: '查询是否支持传入参数effectId',
    onPress: handleSupportEffect,
  },
  {
    id: 1,
    text: '根据VibrateTime触发马达振动',
    onPress: handleStarVibrateTime,
  },
  {
    id: 2,
    text: '根据VibratePreset触发马达振动',
    onPress: handleStartVibratePreset,
  },
  {
    id: 3,
    text: '根据VibrateFromFile触发马达振动',
    onPress: handleStartVibrateFromFile,
  },
  {
    id: 4,
    text: '按照指定模式停止(time)马达振动',
    onPress: handleStopVibrationByMode,
  },
  {
    id: 5,
    text: 'stopVibration停止所有马达振动',
    onPress: handleStopVibration,
  },
  {
    id: 6,
    text: 'stopVibrationSync停止所有马达振动',
    onPress: handleStopVibrationSync,
  },
];

const VibratorTest = (): JSX.Element => {
  const selectColor = (index: number): { bgColor: ColorValue; textColor: ColorValue } => {
    return {
      bgColor: activeIndex === index ? 'rgb(10, 89, 247)' : 'rgba(0, 0, 0, 0.05)',
      textColor: activeIndex === index ? 'white' : 'rgb(10, 89, 247)',
    };
  };
  const touchItem = (text: string, index: number, onPress: () => void): JSX.Element => {
    const itemPress = (): void => {
      onPress();
      setActiveIndex(index);
    };
    return (
      <TouchableOpacity
        key={index + '--' + text}
        style={[{ backgroundColor: selectColor(index).bgColor }, styles.button]}
        onPress={itemPress}>
        <Text style={[{ color: selectColor(index).textColor }, styles.btnText]}>{text}</Text>
      </TouchableOpacity>
    );
  };
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <View style={styles.container}>
      <View style={styles.title}>
        <Text style={styles.titleText}>Vibrator Test</Text>
      </View>

      <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
        {dataList.map((item) => {
          return touchItem(item.text, item.id, item.onPress);
        })}
      </ScrollView>
    </View>
  );
};

export default VibratorTest;

const styles = StyleSheet.create({
  title: {
    flexDirection: 'row',
    justifyContent: 'flex-start',
    width: '100%',
    marginBottom: 16,
  },
  titleText: {
    fontSize: 30,
  },
  container: {
    backgroundColor: '#F1F3F5',
    paddingHorizontal: 16,
    paddingTop: 72,
    justifyContent: 'flex-start',
    alignItems: 'center',
    width: '100%',
    height: '100%',
  },
  scrollView: {
    width: '100%',
  },
  geometryTransitionView: {
    marginVertical: 10,
    height: 200,
    width: 300,
  },
  textinput: {
    backgroundColor: 'white',
    width: '100%',
    minHeight: 120,
    marginBottom: 48,
    borderRadius: 16,
    paddingHorizontal: 16,
    paddingVertical: 8,
    color: 'rgba(0, 0, 0, 0.6)',
  },
  button: {
    width: '100%',
    height: 40,
    borderRadius: 20,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    marginBottom: 12,
  },
  btnText: {
    fontSize: 16,
    fontWeight: '500',
  },
});

```

## Link

目前OpenHarmony暂不支持AutoLink，所以Link步骤需要手动配置。

首先需要使用DevEco Studio打开项目里的OpenHarmony工程，在工程根目录的`oh-package.json5`添加overrides字段：

```json
{
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}
```

### 引鸿蒙端代码

目前有两种方法：

1. **通过har包引入（在IDE完善相关功能后该方法会被遗弃，目前首选此方法）。**

    >**说明：**
    >har包位于三方库安装路径的`harmony`文件夹下。

    a. 打开`entry/oh-package.json5`，添加以下依赖：

    ```
    "dependencies":{
        "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
        "@hadss/react_native_vibrator": "file:../../node_modules/@hadss/react_native_vibrator/harmony/vibrator.har",
      }
    ```

    b. 配置CMakeLists和引入RNOHGeneratedPackage：

    打开`entry/src/main/cpp/CMakeLists.txt`，添加：

    ```diff
    project(rnapp)
    cmake_minimum_required(VERSION 3.4.1)
    set(CMAKE_SKIP_BUILD_RPATH TRUE)
    set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
    set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
    
    set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
    set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
    set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
    set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
    add_compile_definitions(WITH_HITRACE_SYSTRACE)
    set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
    + file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
    
    add_subdirectory("${RNOH_CPP_DIR}" ./rn)
    
    add_library(rnoh_app SHARED
    +    ${GENERATED_CPP_FILES}
        "./PackageProvider.cpp"
        "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
    )
    
    target_link_libraries(rnoh_app PUBLIC rnoh)
    ```

    c. 打开`entry/src/main/cpp/PackageProvider.cpp`，添加：

    ```diff
    #include "RNOH/PackageProvider.h"
    + #include "generated/RNOHGeneratedPackage.h"
    
    using namespace rnoh;
    
    std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
        return {
    +        std::make_shared<RNOHGeneratedPackage>(ctx)
        };
    }
    ```

    d. 在ArkTs侧引入VibratorModulePackage：

    打开`entry/src/main/ets/RNPackagesFactory.ts`，添加：

    ```diff
      ...
    + import { VibratorModulePackage } from '@hadss/react_native_vibrator';
    
    export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
      return [
    +   new VibratorModulePackage(ctx),
      ];
    }
    ```
    e. 应用配置文件module.json5 配置新增：
       目标应用需要在应用配置文件中，配置权限。
    ```
        "requestPermissions": [
          {
            "name": 'ohos.permission.VIBRATE',
            "usedScene": {
              "abilities": [
                "EntryAbility"
              ],
              "when": "inuse"
            }
          }
        ],
    ```

    f. 运行：

    点击右上角的`sync`按钮

    或者在终端执行：

    ```bash
    cd entry
    ohpm install
    ```
    然后编译、运行即可。
> **说明**
> 若项目启动时报错：can not find record '&@rnoh/react-native-openharmony/generated/ts&X.X.X'。需在entry\oh_modules@rnoh\react-native-openharmony\ts.ts文件中添加export * from './generated/ts'，并删除.cxx文件夹、build文件夹，然后执行sync操作同步代码。

2. **直接链接源码。**

    如需使用直接链接源码，请参考[直接链接源码说明](https://gitee.com/react-native-oh-library/usage-docs/blob/master/zh-cn/link-source-code.md)。

##  API

>**说明：**
>"Platform"列表示支持的平台，All表示支持所有平台。


### 振动接口

**Vibrator**
| Name                | Description                                          | Type     | Platform  |
| ------------------- | -----------------------------------------------------| -------- | --------- |
| isSupportEffect     | 判断设备是否支持指定的effectId振动。                    |(effectId: string): Promise<boolean>  | OpenHarmony|
| startVibration      | 根据指定的振动效果和振动属性触发马达振动。                                            |(effect: VibrateEffect,attribute: VibrateAttribute): Promise<void>  | OpenHarmony|
| stopVibration       | 停止(指定模式/所有的)振动。                            | (stopMode?: VibratorStopMode): Promise<void> | OpenHarmony|
| stopVibrationSync   | 停止所有的振动。                                       | function | OpenHarmony|

**说明：**
startVibration支持的文件类型振动，需要将文件放入原生端工程rawFile目录下。
参考资料：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/vibrator

**VibrateEffect**
| Name        | Description                                                      | Type       | Platform  |
| :---------- | :----------------------------------------------------------------| :--------- | :-------- |
|VibratePreset| 按照预置振动效果触发马达振动，适用于交互反馈类的短振场景（如点击长按，滑动，拖拽等），为确保与系统整体振感反馈体验风格一致，推荐使用此接口。                                                   | VibratePreset     | OpenHarmony |
|VibrateFromFile| 按照文件形式定制自定义振动效果触发马达振动，适用于匹配复杂场景效果的交互反馈（如表情包触发的拟真效果、游戏场景/操作反馈）                                            | VibrateFromFile     | OpenHarmony |
|VibrateTime| 按照指定时长触发马达振动，仅对振动时长进行启动或停止控制，满足基础功能，无法对振动强度、频率等维度进行个性化设置，此种振动调节不够细腻，无法满足精致体验                         | VibrateTime     | OpenHarmony |
| description   | 数据记录的描述。缺省为空字符串。                                                   | string     | OpenHarmony |

**VibratePreset**
| Name | Description | Type | Platform |
| :--- | :-------    | :--- | :------- |
|type|按照预置振动效果触发马达振动|'preset'|OpenHarmony|
|effectId|预置的振动效果ID，字符串最大长度64，超出截取64|string|OpenHarmony|
|count|可选参数，振动的重复次数，默认值为1|number|OpenHarmony|
|intensity|可选参数，振动调节强度，取值范围(0,100]内所有整数，默认值为100。若振动效果不支持强度调节或设备不支持时，则按默认强度振动。|number|OpenHarmony|

**VibrateTime**
| Name | Description | Type | Platform |
| :--- | :-------    | :--- | :------- |
|type|按照指定时长触发马达振动。|'time'|OpenHarmony|
|duration|马达持续振动时长, 单位ms。取值范围(0,1800000]区间内所有整数|number|OpenHarmony|

**VibrateFromFile**
| Name | Description | Type | Platform |
| :--- | :-------    | :--- | :------- |
|type|按照指定时长触发马达振动。|'file'|OpenHarmony|
|fileName|位于rawFile目录下的文件名称。文件定义请参阅[自定义配置](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/vibrator-guidelines)|string|OpenHarmony|

**VibrateAttribute**
| Name | Description | Type | Platform |
| :--- | :-------    | :--- | :------- |
|id|可选参数，马达ID， 默认值为0|number|OpenHarmony|
|usage|马达振动的使用场景。默认值为'unknown'，取值范围只允许在Usage提供的类型中选取。|Usage|OpenHarmony|

**Usage**
| Name | Description | Type | Platform |
| :--- | :-------    | :--- | :------- |
|'unknown'|没有明确使用场景，最低优先级，值固定为'unknown'字符串。受触感开关管控，关闭时不振动。|string|OpenHarmony|
|'alarm'|用于警报场景，值固定为'alarm'字符串。受三态开关管控，静音时不振动。|string|OpenHarmony|
|'ring'|用于铃声场景，值固定为'ring'字符串。受三态开关管控，静音时不振动。|string|OpenHarmony|
|'notification'|用于通知场景，值固定为'notification'字符串。受三态开关管控，静音时不振动。|string|OpenHarmony|
|'communication'|用于通信场景，值固定为'communication'字符串。受三态开关管控，静音时不振动。|string|OpenHarmony|
|'touch'|用于触摸场景，值固定为'touch'字符串。 受触感开关管控，关闭时不振动。|string|OpenHarmony|
|'media'|用于多媒体场景，值固定为'media'字符串。受触感开关管控，关闭时不振动。|string|OpenHarmony|
|'physicalFeedback'|用于物理反馈场景，值固定为'physicalFeedback'字符串。受触感开关管控，关闭时不振动。|string|OpenHarmony|
|'simulateReality'|用于模拟现实场景，值固定为'simulateReality'字符串。受触感开关管控，关闭时不振动。|string|OpenHarmony|

**VibratorStopMode**
| Name | Description | Type | Platform |
| :--- | :-------    | :--- | :------- |
|VIBRATOR_STOP_MODE_TIME|停止duration模式的振动。|'time'|OpenHarmony|
|VIBRATOR_STOP_MODE_PRESET|停止预置EffectId的振动。|'preset'|OpenHarmony|

参考资料：https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-vibrator

### 依赖
权限：ohos.permission.VIBRATE

### 约束与限制
本示例仅支持标准系统上运行，支持设备：Phone | PC/2in1 | Tablet | TV | Wearable。
地区限制：仅支持中国境内（不包含中国香港、中国澳门、中国台湾）提供服务。
SDK版本：API12及以上。