@react-native-ohos/react-native-reanimated 3.18.3-beta.2 → 3.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +403 -7
- package/harmony/reanimated/BuildProfile.ets +1 -1
- package/harmony/reanimated/oh-package.json5 +1 -1
- package/harmony/reanimated/src/main/cpp/CMakeLists.txt +2 -0
- package/harmony/reanimated/src/main/cpp/ReanimatedModule.cpp +25 -13
- package/harmony/reanimated/src/main/cpp/ReanimatedSensor.cpp +248 -0
- package/harmony/reanimated/src/main/cpp/ReanimatedSensor.h +36 -0
- package/harmony/reanimated/src/main/cpp/ReanimatedSensorContainer.cpp +36 -0
- package/harmony/reanimated/src/main/cpp/ReanimatedSensorContainer.h +23 -0
- package/harmony/reanimated/src/main/cpp/patches/NativeModules/ReanimatedModuleProxy.cpp +31 -0
- package/harmony/reanimated/src/main/ets/ReanimatedModule.ts +27 -2
- package/harmony/reanimated.har +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,406 @@
|
|
|
1
|
-
|
|
2
|
-
This project is based on [react-native-reanimated@v3.18.0](https://github.com/software-mansion/react-native-reanimated/tree/3.18.0)
|
|
1
|
+
> 文档模板:v0.4.2
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<h1 align="center"> <code>react-native-reanimated</code> </h1>
|
|
5
|
+
</p>
|
|
6
6
|
|
|
7
|
-
[
|
|
7
|
+
本项目基于 [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) 开发。
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:`@react-native-ohos/react-native-reanimated` 版本所属关系如下:
|
|
10
|
+
|
|
11
|
+
| 三方库名称 | 三方库版本(npm地址) | 发布信息 | 支持RN版本 | Autolink | 编译API版本 | 社区基线版本 | 源码地址 |
|
|
12
|
+
| ------------ | ------------ | ------------------------------ | ------------- | ------------- |------------------------ | ------------- | ------------- |
|
|
13
|
+
| @react-native-ohos/react-native-reanimated | [~ 3.18.1](https://www.npmjs.com/package/@react-native-ohos/react-native-reanimated) | [Gitcode Releases](https://gitcode.com/CPF-RN/rntpc_react-native-reanimated/releases) | 0.77.* | 否 | API12+ | 3.18.0 | [br_rnoh0.77](https://gitcode.com/CPF-RN/rntpc_react-native-reanimated/tree/br_rnoh0.77) |
|
|
14
|
+
|
|
15
|
+
## 简介
|
|
16
|
+
|
|
17
|
+
react-native-reanimated 是面向 React Native 生态的第三方原生库,重构实现跨平台动画底层能力,提供声明式 JS 动画 API,实现运行在 UI 线程的高性能流畅动画交互。
|
|
18
|
+
|
|
19
|
+
## 下载安装
|
|
20
|
+
|
|
21
|
+
进入到工程目录并输入以下命令:
|
|
22
|
+
|
|
23
|
+
**npm**
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install react-native-reanimated@3.18.0
|
|
27
|
+
npm install @react-native-ohos/react-native-reanimated
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**yarn**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
yarn add react-native-reanimated@3.18.0
|
|
34
|
+
yarn add @react-native-ohos/react-native-reanimated
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### 插件配置
|
|
38
|
+
|
|
39
|
+
添加插件到 `babel.config.js`:
|
|
40
|
+
|
|
41
|
+
> [!TIP] 为什么需要添加这个?Reanimated Babel 插件会自动转换特殊的 JavaScript 函数(称为 worklet),以便它们可以被传递并在 UI 线程上运行。
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
module.exports = {
|
|
45
|
+
presets: [
|
|
46
|
+
... // don't add it here :)
|
|
47
|
+
],
|
|
48
|
+
plugins: [
|
|
49
|
+
...
|
|
50
|
+
'react-native-reanimated/plugin',
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
清除 Metro 的缓存(推荐):
|
|
56
|
+
|
|
57
|
+
**npm**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm start --reset-cache
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**yarn**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
yarn start --reset-cache
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Link
|
|
70
|
+
|
|
71
|
+
| | 是否支持autolink | RN框架版本 |
|
|
72
|
+
|--------------------------------------|----------------|-----------|
|
|
73
|
+
| ~ 3.18.1 | No | 0.77 |
|
|
74
|
+
|
|
75
|
+
首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 `harmony`。
|
|
76
|
+
|
|
77
|
+
### 1. Overrides RN SDK
|
|
78
|
+
|
|
79
|
+
为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 `oh-package.json5` 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。
|
|
80
|
+
|
|
81
|
+
关于该字段的作用请阅读[官方说明](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-oh-package-json5-V5#zh-cn_topic_0000001792256137_overrides)
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
...
|
|
86
|
+
"overrides": {
|
|
87
|
+
"@rnoh/react-native-openharmony" : "./react_native_openharmony"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 2. 引入原生端代码
|
|
93
|
+
|
|
94
|
+
目前有两种方法:
|
|
95
|
+
|
|
96
|
+
- 通过 har 包引入;
|
|
97
|
+
- 直接链接源码。
|
|
98
|
+
|
|
99
|
+
方法一:通过 har 包引入(推荐)
|
|
100
|
+
|
|
101
|
+
> [!TIP] har 包位于三方库安装路径的 `harmony` 文件夹下。
|
|
102
|
+
|
|
103
|
+
打开 `entry/oh-package.json5`,添加以下依赖
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
"dependencies": {
|
|
107
|
+
"@rnoh/react-native-openharmony": "file:../react_native_openharmony",
|
|
108
|
+
"@react-native-ohos/react-native-reanimated": "file:../../node_modules/@react-native-ohos/react-native-reanimated/harmony/reanimated.har",
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
点击右上角的 `sync` 按钮
|
|
113
|
+
|
|
114
|
+
或者在命令行终端执行:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cd entry
|
|
118
|
+
ohpm install
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
方法二:直接链接源码
|
|
122
|
+
|
|
123
|
+
> [!TIP] 如需使用直接链接源码,请参考[直接链接源码说明](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/link-source-code.md)
|
|
124
|
+
|
|
125
|
+
### 3. 配置 CMakeLists 和引入 ReanimatedPackage
|
|
126
|
+
|
|
127
|
+
打开 `entry/src/main/cpp/CMakeLists.txt`,添加:
|
|
128
|
+
|
|
129
|
+
```diff
|
|
130
|
+
project(rnapp)
|
|
131
|
+
cmake_minimum_required(VERSION 3.4.1)
|
|
132
|
+
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
|
133
|
+
+ set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
|
|
134
|
+
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
|
|
135
|
+
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
|
|
136
|
+
|
|
137
|
+
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
|
|
138
|
+
|
|
139
|
+
# RNOH_BEGIN: manual_package_linking_1
|
|
140
|
+
add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package)
|
|
141
|
+
|
|
142
|
+
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-reanimated/src/main/cpp" ./reanimated)
|
|
143
|
+
|
|
144
|
+
# RNOH_END: manual_package_linking_1
|
|
145
|
+
|
|
146
|
+
add_library(rnoh_app SHARED
|
|
147
|
+
"./PackageProvider.cpp"
|
|
148
|
+
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
target_link_libraries(rnoh_app PUBLIC rnoh)
|
|
152
|
+
|
|
153
|
+
# RNOH_BEGIN: manual_package_linking_2
|
|
154
|
+
target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
|
|
155
|
+
+ target_link_libraries(rnoh_app PUBLIC rnoh_reanimated)
|
|
156
|
+
|
|
157
|
+
# RNOH_END: manual_package_linking_2
|
|
158
|
+
```
|
|
159
|
+
> [!Tip] 注意:上面 NODE_MODULES 定义,为源库的安装路径,用户可以根据安装源库的路径定义 NODE_MODULES。
|
|
160
|
+
|
|
161
|
+
打开 `entry/src/main/cpp/PackageProvider.cpp`,添加:
|
|
162
|
+
|
|
163
|
+
```diff
|
|
164
|
+
#include "RNOH/PackageProvider.h"
|
|
165
|
+
#include "SamplePackage.h"
|
|
166
|
+
+ #include "ReanimatedPackage.h"
|
|
167
|
+
using namespace rnoh;
|
|
168
|
+
|
|
169
|
+
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
|
|
170
|
+
return {
|
|
171
|
+
std::make_shared<SamplePackage>(ctx),
|
|
172
|
+
+ std::make_shared<ReanimatedPackage>(ctx)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 4. 在 ArkTs 侧引入 ReanimatedPackage
|
|
178
|
+
|
|
179
|
+
打开 `entry/src/main/ets/RNPackagesFactory.ts`,添加:
|
|
180
|
+
|
|
181
|
+
```diff
|
|
182
|
+
...
|
|
183
|
+
+ import { ReanimatedPackage} from '@react-native-ohos/react-native-reanimated/ts';
|
|
184
|
+
|
|
185
|
+
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
|
|
186
|
+
return [
|
|
187
|
+
new SamplePackage(ctx),
|
|
188
|
+
+ new ReanimatedPackage(ctx),
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 运行
|
|
194
|
+
|
|
195
|
+
点击右上角的 `sync` 按钮
|
|
196
|
+
|
|
197
|
+
或者在命令行终端执行:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
cd entry
|
|
201
|
+
ohpm install
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
然后编译、运行即可。
|
|
205
|
+
|
|
206
|
+
## 约束与限制
|
|
207
|
+
|
|
208
|
+
### 兼容性
|
|
209
|
+
|
|
210
|
+
本文档内容基于以下版本验证通过:
|
|
211
|
+
|
|
212
|
+
1. RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112 SP15;
|
|
213
|
+
|
|
214
|
+
### 权限要求
|
|
215
|
+
|
|
216
|
+
> [!Tip] 使用传感器的功能,需要使用 >=3.18.3 的版本,如需使用传感器(如加速度传感器、陀螺仪),需要添加对应的传感器权限。
|
|
217
|
+
|
|
218
|
+
在 `entry` 目录下的 `module.json5` 中添加权限
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
"requestPermissions": [
|
|
222
|
+
+ {
|
|
223
|
+
+ "name": "ohos.permission.ACCELEROMETER", // 加速传感器权限名称
|
|
224
|
+
+ "reason": "$string:accele_reason",
|
|
225
|
+
+ "usedScene": {
|
|
226
|
+
+ "abilities": [
|
|
227
|
+
+ "EntryAbility"
|
|
228
|
+
+ ],
|
|
229
|
+
+ "when":"inuse"
|
|
230
|
+
+ }
|
|
231
|
+
+ },
|
|
232
|
+
+ {
|
|
233
|
+
+ "name": "ohos.permission.GYROSCOPE", // 陀螺仪传感器权限名称
|
|
234
|
+
+ "reason": "$string:gyr_reason",
|
|
235
|
+
+ "usedScene": {
|
|
236
|
+
+ "abilities": [
|
|
237
|
+
+ "EntryAbility"
|
|
238
|
+
+ ],
|
|
239
|
+
+ "when":"inuse"
|
|
240
|
+
+ }
|
|
241
|
+
+ },
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
在 entry 目录下添加申请以上权限的原因
|
|
246
|
+
打开 `entry/src/main/resources/base/element/string.json`,添加:
|
|
247
|
+
```
|
|
248
|
+
{
|
|
249
|
+
"string": [
|
|
250
|
+
+ {
|
|
251
|
+
+ "name": "accele_reason",
|
|
252
|
+
+ "value": "使用加速度传感器"
|
|
253
|
+
+ },
|
|
254
|
+
+ {
|
|
255
|
+
+ "name": "gyr_reason",
|
|
256
|
+
+ "value": "使用陀螺仪传感器"
|
|
257
|
+
+ },
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 编译运行API要求
|
|
263
|
+
|
|
264
|
+
> [!TIP] 当前三方库所有版本均已实现版本隔离,支持在 `API12+` 工程编译,及 `API12+` ROM 运行。
|
|
265
|
+
|
|
266
|
+
> [!TIP] 以下功能依赖特定版本的API,使用 `低于指定API版本的工程编译` 或 `低于指定API版本的ROM运行` 均可能导致部分功能受限。
|
|
267
|
+
1. `3.18.3+` 版本新增支持`useReducedMotion` API。该能力依托系统提供的 `isAnimationReduceEnabledSync` API 实现,用于检测设备是否开启减弱动效(reduced motion)辅助功能;该接口仅在 `API23 及以上版本` 的 ROM 环境中生效
|
|
268
|
+
|
|
269
|
+
## 使用示例
|
|
270
|
+
|
|
271
|
+
下面的代码展示了这个库的基本使用场景:
|
|
272
|
+
|
|
273
|
+
> [!WARNING] 使用时 import 的库名不变。
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
import { Button, View } from "react-native";
|
|
277
|
+
import Animated, { useSharedValue, withSpring } from "react-native-reanimated";
|
|
278
|
+
|
|
279
|
+
export default function App() {
|
|
280
|
+
const width = useSharedValue(100);
|
|
281
|
+
|
|
282
|
+
const handlePress = () => {
|
|
283
|
+
width.value = withSpring(width.value + 50);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<View style={{ flex: 1, alignItems: "center" }}>
|
|
288
|
+
<Animated.View
|
|
289
|
+
style={{
|
|
290
|
+
width,
|
|
291
|
+
height: 100,
|
|
292
|
+
backgroundColor: "violet",
|
|
293
|
+
}}
|
|
294
|
+
/>
|
|
295
|
+
<Button onPress={handlePress} title="Click me" />
|
|
296
|
+
</View>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## 接口说明
|
|
302
|
+
|
|
303
|
+
> [!TIP] "平台"列表示该属性在原三方库上支持的平台。
|
|
304
|
+
|
|
305
|
+
> [!TIP] "HarmonyOS平台支持"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
|
|
306
|
+
|
|
307
|
+
### API
|
|
308
|
+
|
|
309
|
+
| 名称 | 类型 | 参数类型 | 返回值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
310
|
+
| ------------------ |----------| -------- | ------ | -------- | -------- | ----------------- | ---- |
|
|
311
|
+
| `withTiming` | function | `(toValue: T, userConfig?: TimingConfig, callback?: AnimationCallback)` | `T` | No | All | yes | withTiming 可基于时长和缓动函数创建动画。 |
|
|
312
|
+
| `withSpring` | function | `(toValue: T, userConfig?: SpringConfig, callback?: AnimationCallback)` | `T` | No | All | yes | withSpring 可创建基于弹簧物理的动画。 |
|
|
313
|
+
| `withDecay` | function | `(userConfig: DecayConfig, callback?: AnimationCallback)` | `number` | No | All | yes | withDecay 可创建模拟带有摩擦力的运动物体的动画。动画将以给定的初速度开始,并根据给定的减速率随时间逐渐减速,直至停止。 |
|
|
314
|
+
| `withRepeat` | function | `(_animation: T, numberOfRepeats?: number, reverse?: boolean, callback?: AnimationCallback)` | `T` | No | All | yes | withRepeat 是一个动画修饰器,可将动画重复指定次数,或无限循环运行。 |
|
|
315
|
+
| `useSharedValue` | function | `(initialValue: Value)` | `SharedValue<Value>` | No | All | yes | useSharedValue 可在组件中定义共享值。 |
|
|
316
|
+
| `useAnimatedStyle` | function | `(updater: () => Style, dependencies?: DependencyList)` | `Style` | No | All | yes | useAnimatedStyle 可创建一个样式对象(类似于 StyleSheet 样式),并可通过共享值对其进行动画处理。 |
|
|
317
|
+
| `useAnimatedRef` | function | `/` | `AnimatedRef<T>` | No | All | yes | useAnimatedRef 可获取视图的引用。可与 measure、scrollTo 和 useScrollViewOffset 函数配合使用。 |
|
|
318
|
+
| `useDerivedValue` | function | `(updater: () => Value, dependencies?: DependencyList)` | `DerivedValue<Value>` | No | All | yes | useDerivedValue 可基于已有的共享值创建新的共享值,并保持其响应性。 |
|
|
319
|
+
| `cancelAnimation` | function | `(sharedValue: SharedValue<TValue>)` | `void` | No | All | yes | cancelAnimation 可取消与某个共享值关联的正在运行的动画。 |
|
|
320
|
+
| `runOnJS` | function | `(fn: Function)` | `(...args: any[]) => void` | No | All | yes | runOnJS 可异步运行未被 worklet 化的函数(这些函数通常无法在 UI 线程上运行)。这适用于大多数外部库,因为它们的函数未标记 "worklet" 指令。 |
|
|
321
|
+
| `runOnUI` | function | `(fn: Function)` | `(...args: any[]) => void` | No | All | yes | runOnUI 可在 UI 线程上异步运行被 worklet 化的函数。 |
|
|
322
|
+
| `measure` | function | `(animatedRef: AnimatedRef<TRef>)` | `MeasuredDimensions \| null` | No | All | yes | measure 可在 UI 线程上同步获取屏幕上视图的尺寸和位置。 |
|
|
323
|
+
| `Easing` | object | `/` | `/` | No | All | yes | 缓动函数,用于设置运动的轨迹。 |
|
|
324
|
+
| `useAnimatedKeyboard` | function | `(options?: AnimatedKeyboardOptions)` | `AnimatedKeyboardInfo` | No | All | yes | useAnimatedKeyboard 可基于虚拟键盘的状态和高度创建动画。 |
|
|
325
|
+
| `useComposedEventHandler`| function | `(handlers: (EventHandlerProcessed \| null)[])` | `ComposedHandlerProcessed` | No | All | yes | 这是一个 Hook,可将基于 useEvent 的事件处理器(如 useAnimatedScrollHandler 或自定义事件处理器)组合为一个单一的事件处理器。 |
|
|
326
|
+
| `makeMutable` | function | `(initial: Value)` | `Mutable<Value>` | No | All | yes | makeMutable 是 useSharedValue Hook 内部用于创建共享值的函数。 |
|
|
327
|
+
| `ReducedMotionConfig` | function | `(config: ReducedMotionConfig)` | `null` | No | All | yes | 根据设备的减弱动效辅助功能设置改变行为。 |
|
|
328
|
+
| `useAnimatedGestureHandler`<sup>deprecated</sup> | function | `(handlers: GestureHandlers, config?: Config)` | `OnGestureEvent` | No | All | No | 可基于手势处理器创建动画。 |
|
|
329
|
+
| `useScrollViewOffset`<sup>deprecated</sup> | function | `(animatedRef: AnimatedRef<AnimatedScrollView>, providedOffset?: SharedValue<number>)` | `SharedValue<number>` | No | All | yes | 可基于 ScrollView 的偏移量创建动画。 |
|
|
330
|
+
| `useReducedMotion`<sup>API>=23</sup> | function | `/` | `boolean` | No | All | yes | React Native Reanimated 库中的一个 Hook,用于检测设备是否启用了减弱动效(reduced motion)辅助功能设置。 |
|
|
331
|
+
|
|
332
|
+
### 属性
|
|
333
|
+
|
|
334
|
+
**WithSpringConfig**
|
|
335
|
+
|
|
336
|
+
| 名称 | 参数类型 | 默认值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
337
|
+
| ----------------------- | ------------------------------------------------------------ | ---------------- | -------- | --------------- | ------------------ | ---- |
|
|
338
|
+
| clamp | `{ min?: number; max?: number }` | None | No | Android and iOS | yes | 限制运动范围。如果弹簧动画会超出此限制,则会降低 `dampingRatio`(使弹簧的弹性减弱) |
|
|
339
|
+
|
|
340
|
+
**SharedValue**
|
|
341
|
+
|
|
342
|
+
| 名称 | 参数类型 | 默认值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
343
|
+
| --------------------- | ------------------------------------------------------------ | -------- | -------- | --------------- | ------------------ | ---- |
|
|
344
|
+
| get | `() => Value` | None | No | Android and iOS | yes | 返回当前值 |
|
|
345
|
+
| set | `(value: Value \| ((value: Value) => Value)) => void` | None | No | Android and iOS | yes | 更新存储的值(支持直接赋值或更新函数) |
|
|
346
|
+
| addListener | `(listenerID: number, listener: (value: Value) => void) => void` | None | No | Android and iOS | yes | 注册值变化监听器 |
|
|
347
|
+
| modify | `(modifier?: (value: Value) => Value, forceUpdate?: boolean) => void` | None | No | Android and iOS | yes | 对当前值应用变换 |
|
|
348
|
+
|
|
349
|
+
**DerivedValue**
|
|
350
|
+
|
|
351
|
+
| 名称 | 参数类型 | 默认值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
352
|
+
| ------------------------------------ | --------------------------- | -------- | -------- | --------------- | ------------------ | ---- |
|
|
353
|
+
| set<sup>deprecated</sup> | None | None | No | Android and iOS | No | 派生值为只读 |
|
|
354
|
+
|
|
355
|
+
**CurrentLayoutAnimationsValues**
|
|
356
|
+
|
|
357
|
+
| 名称 | 参数类型 | 默认值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
358
|
+
| ------------------------------------- | --------------------- | ------ | -------- | --------------- | ------------------ | ---- |
|
|
359
|
+
| currentBorderRadius | number | 0 | No | Android and iOS | No | 视图的圆角半径 |
|
|
360
|
+
|
|
361
|
+
**EntryAnimationsValues**
|
|
362
|
+
|
|
363
|
+
| 名称 | 参数类型 | 默认值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
364
|
+
| ------------------------------------ | --------------------- | ------ | -------- | --------------- | ------------------ | ---- |
|
|
365
|
+
| targetBorderRadius | number | 0 | No | Android and iOS | No | 视图的圆角半径 |
|
|
366
|
+
|
|
367
|
+
**itemLayoutAnimation**
|
|
368
|
+
|
|
369
|
+
| 名称 | 参数类型 | 默认值 | 必填 | 平台 | HarmonyOS平台支持 | 描述 |
|
|
370
|
+
| ------------------------------------- | ----------------------------------------- | --------------- | -------- | --------------- | ------------------ | ---- |
|
|
371
|
+
| itemLayoutAnimation | `ILayoutAnimationBuilder` | None | No | Android and iOS | yes | 定义列表项的布局过渡动画 |
|
|
372
|
+
|
|
373
|
+
## 遗留问题
|
|
374
|
+
|
|
375
|
+
## 其他
|
|
376
|
+
- [React Native中LayoutAnimation与react-native-reanimated存在不兼容,推荐使用react-native-reanimated](https://github.com/software-mansion/react-native-reanimated/issues/6751)
|
|
377
|
+
|
|
378
|
+
## 目录结构
|
|
379
|
+
````
|
|
380
|
+
/rntpc_react-native-reanimated # 项目根目录
|
|
381
|
+
├── harmony # 鸿蒙适配代码
|
|
382
|
+
│ ├── reanimated.har # har 包
|
|
383
|
+
│ └── reanimated # 鸿蒙适配核心代码
|
|
384
|
+
│ ├── index.ets # 鸿蒙适配代码入口
|
|
385
|
+
│ └── src/main
|
|
386
|
+
│ ├── cpp # C++ 原生层(ReanimatedPackage、ReanimatedModule 等)
|
|
387
|
+
│ ├── ets # ArkTS 层
|
|
388
|
+
│ ├── resources # 资源文件
|
|
389
|
+
│ └── module.json5
|
|
390
|
+
├── src # RN / TS 代码
|
|
391
|
+
│ └── index.ts # 入口文件
|
|
392
|
+
├── example # 示例工程
|
|
393
|
+
├── package.json
|
|
394
|
+
├── README.md # 中文安装使用方法
|
|
395
|
+
├── README_en.md # 英文安装使用方法
|
|
396
|
+
├── CHANGELOG.md # 更新日志
|
|
397
|
+
└── OAT.xml # 开源合规
|
|
398
|
+
````
|
|
399
|
+
|
|
400
|
+
## 贡献代码
|
|
401
|
+
|
|
402
|
+
使用过程中发现任何问题都可以提交 [Issue](https://gitcode.com/CPF-RN/rntpc_react-native-reanimated/issues),当然,也非常欢迎提交 [PR](https://gitcode.com/CPF-RN/rntpc_react-native-reanimated/pulls) 。
|
|
403
|
+
|
|
404
|
+
## 开源协议
|
|
405
|
+
|
|
406
|
+
本项目基于 [The MIT License (MIT)](https://github.com/software-mansion/react-native-reanimated/blob/main/LICENSE) ,请自由地享受和参与开源。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Use these variables when you tailor your ArkTS code. They must be of the const type.
|
|
3
3
|
*/
|
|
4
|
-
export const HAR_VERSION = '3.18.3
|
|
4
|
+
export const HAR_VERSION = '3.18.3';
|
|
5
5
|
export const BUILD_MODE_NAME = 'debug';
|
|
6
6
|
export const DEBUG = true;
|
|
7
7
|
export const TARGET_NAME = 'default';
|
|
@@ -69,4 +69,6 @@ target_sources(rnoh_reanimated
|
|
|
69
69
|
|
|
70
70
|
target_include_directories(rnoh_reanimated PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
71
71
|
target_link_libraries(rnoh_reanimated PUBLIC rnoh)
|
|
72
|
+
target_link_libraries(rnoh_reanimated PUBLIC libohsensor.so)
|
|
73
|
+
target_link_libraries(rnoh_reanimated PUBLIC libnative_display_manager.so)
|
|
72
74
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include "RNOH/ArkTSTurboModule.h"
|
|
6
6
|
#include "RNOH/RNInstance.h"
|
|
7
7
|
#include "ReanimatedModule.h"
|
|
8
|
+
#include "ReanimatedSensorContainer.h"
|
|
8
9
|
#include "ReanimatedUIScheduler.h"
|
|
9
10
|
#include "PlatformDepMethodsHolder.h"
|
|
10
11
|
#include "WorkletRuntimeCollector.h"
|
|
@@ -54,12 +55,12 @@ ReanimatedModule::~ReanimatedModule()
|
|
|
54
55
|
|
|
55
56
|
void ReanimatedModule::installTurboModule(facebook::jsi::Runtime &rt)
|
|
56
57
|
{
|
|
57
|
-
auto
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
auto runMainThreadFuc = [weakExecutor = std::weak_ptr(m_ctx.taskExecutor)](TaskExecutor::Task &&task) {
|
|
59
|
+
if (auto taskExecutor = weakExecutor.lock()) {
|
|
60
|
+
taskExecutor->runTask(TaskThread::MAIN, std::move(task));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
auto nodesManager = std::make_shared<ReanimatedNodesManager>(runMainThreadFuc);
|
|
63
64
|
|
|
64
65
|
auto maybeFlushUIUpdatesQueueFunction = [nodesManager]() { nodesManager->maybeFlushUIUpdatesQueue(); };
|
|
65
66
|
auto requestRender = [weakSelf = weak_from_this(), nodesManager](std::function<void(double)> onRender) {
|
|
@@ -87,14 +88,13 @@ void ReanimatedModule::installTurboModule(facebook::jsi::Runtime &rt)
|
|
|
87
88
|
|
|
88
89
|
auto getAnimationTimestamp = getMillisSinceEpoch;
|
|
89
90
|
|
|
90
|
-
auto
|
|
91
|
+
auto sensorContainer = std::make_shared<ReanimatedSensorContainer>();
|
|
92
|
+
auto registerSensorFunction = [sensorContainer,
|
|
93
|
+
runMainThreadFuc](int sensorType, int interval, int iosReferenceFrame,
|
|
91
94
|
std::function<void(double[], int)> setter) -> int {
|
|
92
|
-
|
|
93
|
-
return -1;
|
|
94
|
-
};
|
|
95
|
-
auto unregisterSensorFunction = [](int sensorId) {
|
|
96
|
-
// TODO
|
|
95
|
+
return sensorContainer->registerSensor(sensorType, interval, iosReferenceFrame, setter, runMainThreadFuc);
|
|
97
96
|
};
|
|
97
|
+
auto unregisterSensorFunction = [sensorContainer](int sensorId) { sensorContainer->unregisterSensor(sensorId); };
|
|
98
98
|
auto subscribeForKeyboardEventsFunction =
|
|
99
99
|
[weakSelf = weak_from_this()](std::function<void(int keyboardState, int height)> keyboardEventDataUpdater,
|
|
100
100
|
bool isStatusBarTranslucent, bool isNavigationBarTranslucent) {
|
|
@@ -153,7 +153,19 @@ void ReanimatedModule::installTurboModule(facebook::jsi::Runtime &rt)
|
|
|
153
153
|
unsubscribeFromKeyboardEventsFunction,
|
|
154
154
|
maybeFlushUIUpdatesQueueFunction,
|
|
155
155
|
};
|
|
156
|
-
|
|
156
|
+
bool isReducedMotion = false;
|
|
157
|
+
m_ctx.taskExecutor->runSyncTask(TaskThread::MAIN,
|
|
158
|
+
[ctx = m_ctx, &isReducedMotion]() {
|
|
159
|
+
if (!ctx.arkTSTurboModuleInstanceRef) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
ArkJS arkJs(ctx.env);
|
|
163
|
+
auto napiTurboModuleObject =
|
|
164
|
+
arkJs.getObject(ctx.arkTSTurboModuleInstanceRef);
|
|
165
|
+
auto napiResult =
|
|
166
|
+
napiTurboModuleObject.call("isReducedMotionEnabled", {});
|
|
167
|
+
isReducedMotion = arkJs.getBoolean(napiResult);
|
|
168
|
+
});
|
|
157
169
|
auto isBridgeless = true;
|
|
158
170
|
auto workletsModule = [weakSelf = weak_from_this()]() -> std::shared_ptr<WorkletsModule> {
|
|
159
171
|
auto self = weakSelf.lock();
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
|
|
2
|
+
// Use of this source code is governed by a MIT license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
#include "ReanimatedSensor.h"
|
|
6
|
+
#include "glog/logging.h"
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <vector>
|
|
9
|
+
#include <sensors/oh_sensor.h>
|
|
10
|
+
#include <window_manager/oh_display_info.h>
|
|
11
|
+
#include <window_manager/oh_display_manager.h>
|
|
12
|
+
namespace rnoh {
|
|
13
|
+
static std::unordered_map<Sensor_Type, ReanimatedSensor *> g_sensorSubscriberMap_;
|
|
14
|
+
static std::mutex g_subscriberMapMutex_; // 保护映射表的互斥锁
|
|
15
|
+
|
|
16
|
+
void SensorDataCallbackImpl(Sensor_Event *event) {
|
|
17
|
+
if (event == nullptr) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
int64_t timestamp = -1;
|
|
21
|
+
// 获取传感器数据的时间戳。
|
|
22
|
+
int32_t ret = OH_SensorEvent_GetTimestamp(event, ×tamp);
|
|
23
|
+
if (ret != SENSOR_SUCCESS) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
Sensor_Type sensorType;
|
|
27
|
+
// 获取传感器类型。
|
|
28
|
+
ret = OH_SensorEvent_GetType(event, &sensorType);
|
|
29
|
+
if (ret != SENSOR_SUCCESS) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
Sensor_Accuracy accuracy = SENSOR_ACCURACY_UNRELIABLE;
|
|
33
|
+
// 获取传感器数据的精度。
|
|
34
|
+
ret = OH_SensorEvent_GetAccuracy(event, &accuracy);
|
|
35
|
+
if (ret != SENSOR_SUCCESS) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
float *data = nullptr;
|
|
39
|
+
uint32_t length = 0;
|
|
40
|
+
// 获取传感器数据。
|
|
41
|
+
ret = OH_SensorEvent_GetData(event, &data, &length);
|
|
42
|
+
if (ret != SENSOR_SUCCESS || data == nullptr) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
std::lock_guard<std::mutex> lock(g_subscriberMapMutex_);
|
|
47
|
+
auto it = g_sensorSubscriberMap_.find(sensorType);
|
|
48
|
+
if (it == g_sensorSubscriberMap_.end()) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
ReanimatedSensor *sensorInstance = it->second;
|
|
52
|
+
if (sensorInstance == nullptr) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
switch (sensorType) {
|
|
57
|
+
case SENSOR_TYPE_ACCELEROMETER:
|
|
58
|
+
case SENSOR_TYPE_GYROSCOPE:
|
|
59
|
+
case SENSOR_TYPE_GRAVITY:
|
|
60
|
+
case SENSOR_TYPE_MAGNETIC_FIELD: {
|
|
61
|
+
double sendData[] = {
|
|
62
|
+
data[0],
|
|
63
|
+
data[1],
|
|
64
|
+
data[2],
|
|
65
|
+
};
|
|
66
|
+
// 传入 count,由 runSensor 拷贝到堆,避免跨线程 use-after-free
|
|
67
|
+
sensorInstance->runSensor(sendData, 3);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
case SENSOR_TYPE_ROTATION_VECTOR: {
|
|
71
|
+
double sendData[] = {
|
|
72
|
+
data[0], // x
|
|
73
|
+
data[1], // y
|
|
74
|
+
data[2], // z
|
|
75
|
+
data[3], // w
|
|
76
|
+
// ios
|
|
77
|
+
0, // yaw
|
|
78
|
+
0, // pitch
|
|
79
|
+
0 // roll
|
|
80
|
+
};
|
|
81
|
+
sensorInstance->runSensor(sendData, 7);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
rnoh::ReanimatedSensor::ReanimatedSensor(int sensorType, int interval, int iosReferenceFrame,
|
|
91
|
+
std::function<void(double *, int)> setter,
|
|
92
|
+
std::function<void(TaskExecutor::Task &&)> runOnTheMainThread)
|
|
93
|
+
: iosReferenceFrame_(iosReferenceFrame), setter_(std::move(setter)),
|
|
94
|
+
runOnTheMainThread_(std::move(runOnTheMainThread)) {
|
|
95
|
+
if (interval == -1) {
|
|
96
|
+
uint32_t refreshRate = 0;
|
|
97
|
+
OH_NativeDisplayManager_GetDefaultDisplayRefreshRate(&refreshRate);
|
|
98
|
+
if (refreshRate > 0) {
|
|
99
|
+
interval_ = refreshRate * 1000000; // 120hz;
|
|
100
|
+
} else {
|
|
101
|
+
interval_ = 200000000;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
// js unit is milliseconds
|
|
105
|
+
interval_ = interval * 1000000;
|
|
106
|
+
}
|
|
107
|
+
castSensorType(sensorType);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
void rnoh::ReanimatedSensor::castSensorType(int sensorType) {
|
|
112
|
+
if (sensorType == 1) {
|
|
113
|
+
sensorType_ = SENSOR_TYPE_ACCELEROMETER;
|
|
114
|
+
} else if (sensorType == 2) {
|
|
115
|
+
sensorType_ = SENSOR_TYPE_GYROSCOPE;
|
|
116
|
+
} else if (sensorType == 3) {
|
|
117
|
+
sensorType_ = SENSOR_TYPE_GRAVITY;
|
|
118
|
+
} else if (sensorType == 4) {
|
|
119
|
+
sensorType_ = SENSOR_TYPE_MAGNETIC_FIELD;
|
|
120
|
+
} else if (sensorType == 5) {
|
|
121
|
+
sensorType_ = SENSOR_TYPE_ROTATION_VECTOR;
|
|
122
|
+
} else {
|
|
123
|
+
throw std::invalid_argument("[ReanimatedSensor] Unknown sensorType: " + sensorType);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
bool rnoh::ReanimatedSensor::initialize() {
|
|
128
|
+
if (!isSupportSensor()) {
|
|
129
|
+
LOG(ERROR) << "[ReanimatedSensor] is not support, sensorType:" << sensorType_;
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
id_ = OH_Sensor_CreateSubscriptionId();
|
|
133
|
+
if (id_ == nullptr) {
|
|
134
|
+
LOG(ERROR) << "[ReanimatedSensor] create sensor id failed";
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
int32_t res = OH_SensorSubscriptionId_SetType(id_, sensorType_);
|
|
138
|
+
if (res != SENSOR_SUCCESS) {
|
|
139
|
+
LOG(ERROR) << "[ReanimatedSensor] set sensor type failed ,sensorType:" << sensorType_;
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
auto attr = OH_Sensor_CreateSubscriptionAttribute();
|
|
143
|
+
if (attr == nullptr) {
|
|
144
|
+
LOG(ERROR) << "[ReanimatedSensor] create sensor attribute failed";
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
res = OH_SensorSubscriptionAttribute_SetSamplingInterval(attr, interval_);
|
|
149
|
+
if (res != SENSOR_SUCCESS) {
|
|
150
|
+
LOG(ERROR) << "[ReanimatedSensor] set sensor Interval failed, interval_:" << interval_;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
subSensor_ = OH_Sensor_CreateSubscriber();
|
|
155
|
+
|
|
156
|
+
{
|
|
157
|
+
std::lock_guard<std::mutex> lock(g_subscriberMapMutex_);
|
|
158
|
+
g_sensorSubscriberMap_[sensorType_] = this;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
int32_t ret = OH_SensorSubscriber_SetCallback(subSensor_, SensorDataCallbackImpl);
|
|
162
|
+
if (ret != SENSOR_SUCCESS) {
|
|
163
|
+
LOG(ERROR) << "[ReanimatedSensor] set sensor callback failed";
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
Sensor_Result sensorRes = OH_Sensor_Subscribe(id_, attr, subSensor_);
|
|
167
|
+
if (sensorRes != SENSOR_SUCCESS) {
|
|
168
|
+
LOG(ERROR) << "[ReanimatedSensor] on sensor subscribe failed";
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
bool rnoh::ReanimatedSensor::isSupportSensor() {
|
|
176
|
+
uint32_t count = 0;
|
|
177
|
+
int32_t ret = OH_Sensor_GetInfos(nullptr, &count);
|
|
178
|
+
bool isSupport = false;
|
|
179
|
+
if (count > 0) {
|
|
180
|
+
Sensor_Info **sensors = OH_Sensor_CreateInfos(count);
|
|
181
|
+
ret = OH_Sensor_GetInfos(sensors, &count);
|
|
182
|
+
for (uint32_t i = 0; i < count; ++i) {
|
|
183
|
+
Sensor_Info *sensorInfoTemp = sensors[i];
|
|
184
|
+
Sensor_Type sensorType;
|
|
185
|
+
ret = OH_SensorInfo_GetType(sensorInfoTemp, &sensorType);
|
|
186
|
+
if (sensorType == sensorType_) {
|
|
187
|
+
isSupport = true;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
ret = OH_Sensor_DestroyInfos(sensors, count);
|
|
192
|
+
}
|
|
193
|
+
return isSupport;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
void rnoh::ReanimatedSensor::runSensor(double *value, size_t count) {
|
|
197
|
+
if (value && setter_ && runOnTheMainThread_ && count > 0) {
|
|
198
|
+
// 跨线程传递必须拷贝到堆:传感器回调运行在传感器线程,
|
|
199
|
+
// 而 setter_ 在主线程异步执行,栈上数据此时已释放(use-after-free)。
|
|
200
|
+
auto dataPtr = std::make_shared<std::vector<double>>(value, value + count);
|
|
201
|
+
runOnTheMainThread_([weakSelf = weak_from_this(), dataPtr]() {
|
|
202
|
+
auto self = weakSelf.lock();
|
|
203
|
+
if (!self) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
self->setter_(dataPtr->data(), self->getInterfaceOrientation());
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
int rnoh::ReanimatedSensor::getInterfaceOrientation() {
|
|
212
|
+
NativeDisplayManager_Rotation rotation;
|
|
213
|
+
auto ret = OH_NativeDisplayManager_GetDefaultDisplayRotation(&rotation);
|
|
214
|
+
if (ret == DISPLAY_MANAGER_OK) {
|
|
215
|
+
switch (rotation) {
|
|
216
|
+
case DISPLAY_MANAGER_ROTATION_270:
|
|
217
|
+
return 270;
|
|
218
|
+
case DISPLAY_MANAGER_ROTATION_90:
|
|
219
|
+
return 90;
|
|
220
|
+
case DISPLAY_MANAGER_ROTATION_180:
|
|
221
|
+
return 180;
|
|
222
|
+
default:
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
void rnoh::ReanimatedSensor::cancel() { destroySensor(); }
|
|
231
|
+
|
|
232
|
+
void rnoh::ReanimatedSensor::destroySensor() {
|
|
233
|
+
std::lock_guard<std::mutex> lock(g_subscriberMapMutex_);
|
|
234
|
+
for (auto it = g_sensorSubscriberMap_.begin(); it != g_sensorSubscriberMap_.end();) {
|
|
235
|
+
if (it->second == this) {
|
|
236
|
+
OH_Sensor_Unsubscribe(it->second->id_, it->second->subSensor_);
|
|
237
|
+
OH_Sensor_DestroySubscriptionId(it->second->id_);
|
|
238
|
+
OH_Sensor_DestroySubscriber(it->second->subSensor_);
|
|
239
|
+
it->second->subSensor_ = nullptr;
|
|
240
|
+
it = g_sensorSubscriberMap_.erase(it);
|
|
241
|
+
} else {
|
|
242
|
+
++it;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
rnoh::ReanimatedSensor::~ReanimatedSensor() { destroySensor(); }
|
|
248
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
|
|
2
|
+
// Use of this source code is governed by a MIT license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
#ifndef HARMONY_REANIMATEDSENSOR_H
|
|
6
|
+
#define HARMONY_REANIMATEDSENSOR_H
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <mutex>
|
|
9
|
+
#include <sensors/oh_sensor_type.h>
|
|
10
|
+
#include "RNOH/TaskExecutor/TaskExecutor.h"
|
|
11
|
+
|
|
12
|
+
namespace rnoh {
|
|
13
|
+
class ReanimatedSensor : public std::enable_shared_from_this<ReanimatedSensor> {
|
|
14
|
+
public:
|
|
15
|
+
ReanimatedSensor(int sensorType, int interval, int iosReferenceFrame, std::function<void(double[], int)> setter,
|
|
16
|
+
std::function<void(TaskExecutor::Task &&)> runOnTheMainThread);
|
|
17
|
+
~ReanimatedSensor();
|
|
18
|
+
bool initialize();
|
|
19
|
+
void cancel();
|
|
20
|
+
void castSensorType(int sensorType);
|
|
21
|
+
void runSensor(double *value, size_t count);
|
|
22
|
+
std::function<void(double[], int)> setter_;
|
|
23
|
+
Sensor_Type sensorType_{SENSOR_TYPE_ACCELEROMETER};
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
void destroySensor();
|
|
27
|
+
bool isSupportSensor();
|
|
28
|
+
int getInterfaceOrientation();
|
|
29
|
+
int interval_{-1};
|
|
30
|
+
int iosReferenceFrame_{-1};
|
|
31
|
+
Sensor_Subscriber *subSensor_{nullptr};
|
|
32
|
+
Sensor_SubscriptionId *id_{nullptr};
|
|
33
|
+
std::function<void(TaskExecutor::Task &&)> runOnTheMainThread_;
|
|
34
|
+
};
|
|
35
|
+
} // namespace rnoh
|
|
36
|
+
#endif // HARMONY_REANIMATEDSENSOR_H
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
|
|
2
|
+
// Use of this source code is governed by a MIT license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
#include "ReanimatedSensorContainer.h"
|
|
6
|
+
#include "glog/logging.h"
|
|
7
|
+
namespace rnoh {
|
|
8
|
+
|
|
9
|
+
rnoh::ReanimatedSensorContainer::ReanimatedSensorContainer() {}
|
|
10
|
+
|
|
11
|
+
int rnoh::ReanimatedSensorContainer::registerSensor(int sensorType, int interval, int iosReferenceFrame,
|
|
12
|
+
std::function<void(double *, int)> setter,
|
|
13
|
+
std::function<void(TaskExecutor::Task &&)> runOnTheMainThread) {
|
|
14
|
+
auto sensor =
|
|
15
|
+
std::make_shared<ReanimatedSensor>(sensorType, interval, iosReferenceFrame, setter, runOnTheMainThread);
|
|
16
|
+
int sensorId = -1;
|
|
17
|
+
if (sensor->initialize()) {
|
|
18
|
+
sensorId = nextSensorId;
|
|
19
|
+
nextSensorId++;
|
|
20
|
+
sensorMap_[sensorId] = sensor;
|
|
21
|
+
}
|
|
22
|
+
return sensorId;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void rnoh::ReanimatedSensorContainer::unregisterSensor(int sensorId) {
|
|
26
|
+
auto it = sensorMap_.find(sensorId);
|
|
27
|
+
if (it == sensorMap_.end()) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
auto sensor = it->second;
|
|
31
|
+
if (sensor != nullptr) {
|
|
32
|
+
sensor->cancel();
|
|
33
|
+
sensorMap_.erase(it);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
|
|
2
|
+
// Use of this source code is governed by a MIT license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
#ifndef HARMONY_REANIMATEDSENSORCONTAINER_H
|
|
6
|
+
#define HARMONY_REANIMATEDSENSORCONTAINER_H
|
|
7
|
+
#include "ReanimatedSensor.h"
|
|
8
|
+
#include <functional>
|
|
9
|
+
namespace rnoh {
|
|
10
|
+
class ReanimatedSensorContainer {
|
|
11
|
+
public:
|
|
12
|
+
ReanimatedSensorContainer();
|
|
13
|
+
~ReanimatedSensorContainer() noexcept = default;
|
|
14
|
+
int registerSensor(int sensorType, int interval, int iosReferenceFrame, std::function<void(double[], int)> setter,
|
|
15
|
+
std::function<void(TaskExecutor::Task &&)> runOnTheMainThread);
|
|
16
|
+
void unregisterSensor(int sensorId);
|
|
17
|
+
|
|
18
|
+
private:
|
|
19
|
+
std::unordered_map<int, std::shared_ptr<ReanimatedSensor>> sensorMap_;
|
|
20
|
+
int nextSensorId{0};
|
|
21
|
+
};
|
|
22
|
+
} // namespace rnoh
|
|
23
|
+
#endif // HARMONY_REANIMATEDSENSORCONTAINER_H
|
|
@@ -256,6 +256,37 @@ ReanimatedModuleProxy::~ReanimatedModuleProxy() {
|
|
|
256
256
|
}
|
|
257
257
|
layoutAnimationsProxy_.reset();
|
|
258
258
|
|
|
259
|
+
// 主动触发 Hermes GC:在 uiWorkletRuntime_ 销毁前回收 Hermes 堆中持有
|
|
260
|
+
// ShadowNode::Shared(通过 NativeState / ShadowNodeWrapper)的 JSI 对象。
|
|
261
|
+
//
|
|
262
|
+
// 此处 commitHook_ / mountHook_ 仍未 reset,它们持有 uiManager_ 的
|
|
263
|
+
// shared_ptr,确保 GC 回收 ShadowNodeWrapper 触发 ~ShadowNodeFamily 时
|
|
264
|
+
// ComponentDescriptorRegistry 仍然有效。这样可避免 uiWorkletRuntime_.reset()
|
|
265
|
+
// → HadesGC::finalizeAll() 在上下文可能已失效后触发
|
|
266
|
+
// ~ShadowNodeFamily → ~InstanceHandle 崩溃(0730 复现变体根因)。
|
|
267
|
+
//
|
|
268
|
+
// UI runtime 为 LockableRuntime(supportsLocking=true),每个 JSI 操作
|
|
269
|
+
// 自动加锁 runtimeMutex_(recursive_mutex),从主线程调用 gc() 会阻塞
|
|
270
|
+
// 等待 UI 线程完成当前 worklet 后执行,不会死锁。
|
|
271
|
+
#if JS_RUNTIME_HERMES
|
|
272
|
+
if (uiWorkletRuntime_) {
|
|
273
|
+
try {
|
|
274
|
+
auto &uiRuntime = uiWorkletRuntime_->getJSIRuntime();
|
|
275
|
+
auto gc = uiRuntime.global().getPropertyAsFunction(uiRuntime, "gc");
|
|
276
|
+
gc.call(uiRuntime);
|
|
277
|
+
} catch (const std::exception &) {
|
|
278
|
+
// gc() 调用失败不应阻止后续析构流程
|
|
279
|
+
} catch (...) {
|
|
280
|
+
// 忽略未知异常,保证析构不因 GC 失败而 terminate
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
#endif // JS_RUNTIME_HERMES
|
|
284
|
+
|
|
285
|
+
// GC 之后再注销 hooks:确保 GC 回收 ShadowNodeWrapper 时
|
|
286
|
+
// UIManager(及 ComponentDescriptorRegistry)仍存活
|
|
287
|
+
commitHook_.reset();
|
|
288
|
+
mountHook_.reset();
|
|
289
|
+
uiManager_.reset();
|
|
259
290
|
#endif // RCT_NEW_ARCH_ENABLED
|
|
260
291
|
uiWorkletRuntime_.reset();
|
|
261
292
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { RNOHLogger, Tag, UITurboModule, UITurboModuleContext } from "@rnoh/react-native-openharmony/ts";
|
|
6
6
|
import { Animator, AnimatorResult, window } from '@kit.ArkUI';
|
|
7
7
|
import { KeyboardState } from "./AnimationManager";
|
|
8
|
+
import deviceInfo from '@ohos.deviceInfo';
|
|
9
|
+
import { accessibility } from '@kit.AccessibilityKit';
|
|
8
10
|
|
|
9
11
|
export class ReanimatedModule extends UITurboModule {
|
|
10
12
|
static NAME = "ReanimatedModule"
|
|
@@ -24,7 +26,13 @@ export class ReanimatedModule extends UITurboModule {
|
|
|
24
26
|
|
|
25
27
|
public async subscribeKeyBordListeners(isStatusBarTranslucent: boolean, isNavigationBarTranslucent: boolean) {
|
|
26
28
|
//ignore isStatusBarTranslucent - those are Android only
|
|
27
|
-
|
|
29
|
+
let windowInstance: window.Window;
|
|
30
|
+
try {
|
|
31
|
+
windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
|
|
32
|
+
} catch (e: ESObject) {
|
|
33
|
+
this.logger.error(`subscribeKeyBordListeners failed: ${e?.message ?? e}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
28
36
|
const navIndicatorArea = windowInstance.getWindowAvoidArea(
|
|
29
37
|
window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
|
|
30
38
|
);
|
|
@@ -91,7 +99,13 @@ export class ReanimatedModule extends UITurboModule {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
public async unsubscribeKeyBordListeners() {
|
|
94
|
-
|
|
102
|
+
let windowInstance: window.Window;
|
|
103
|
+
try {
|
|
104
|
+
windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
|
|
105
|
+
} catch (e: ESObject) {
|
|
106
|
+
this.logger.error(`unsubscribeKeyBordListeners failed: ${e?.message ?? e}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
95
109
|
windowInstance.off('keyboardHeightChange')
|
|
96
110
|
if (this.currentAnimator) {
|
|
97
111
|
this.currentAnimator.onFrame = null;
|
|
@@ -120,4 +134,15 @@ export class ReanimatedModule extends UITurboModule {
|
|
|
120
134
|
}
|
|
121
135
|
return isLayoutFullScreen;
|
|
122
136
|
}
|
|
137
|
+
|
|
138
|
+
public isReducedMotionEnabled(): boolean {
|
|
139
|
+
try {
|
|
140
|
+
if (deviceInfo.sdkApiVersion >= 23) {
|
|
141
|
+
return (accessibility as ESObject).isAnimationReduceEnabledSync();
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
this.logger.warn(`isReducedMotionEnabled failed: ${JSON.stringify(e)}`);
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
123
148
|
}
|
package/harmony/reanimated.har
CHANGED
|
Binary file
|