@react-native-ohos/react-native-reanimated 3.18.2-rc.2 → 3.18.3-beta.2

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 CHANGED
@@ -2,9 +2,9 @@
2
2
  This project is based on [react-native-reanimated@v3.18.0](https://github.com/software-mansion/react-native-reanimated/tree/3.18.0)
3
3
 
4
4
  ## Documentation
5
- [中文](https://gitee.com/react-native-oh-library/usage-docs/blob/master/zh-cn/react-native-reanimated.md)
5
+ [中文](https://gitcode.com/OpenHarmony-RN/usage-docs/blob/master/zh-cn/react-native-reanimated.md)
6
6
 
7
- [English](https://gitee.com/react-native-oh-library/usage-docs/blob/master/en/react-native-reanimated.md)
7
+ [English](https://gitcode.com/OpenHarmony-RN/usage-docs/blob/master/en/react-native-reanimated.md)
8
8
 
9
9
  ## License
10
10
  This library is licensed under [The MIT License (MIT)](https://github.com/react-native-oh-library/react-native-harmony-reanimated/blob/sig/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.2-rc.2';
4
+ export const HAR_VERSION = '3.18.3-beta.2';
5
5
  export const BUILD_MODE_NAME = 'debug';
6
6
  export const DEBUG = true;
7
7
  export const TARGET_NAME = 'default';
@@ -0,0 +1,29 @@
1
+ {
2
+ "meta": {
3
+ "stableOrder": true
4
+ },
5
+ "lockfileVersion": 3,
6
+ "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
7
+ "specifiers": {
8
+ "@ppd/ffrt@1.1.5": "@ppd/ffrt@1.1.5",
9
+ "@rnoh/react-native-openharmony@../../example/node_modules/react-native-harmony/react_native_openharmony.har": "@rnoh/react-native-openharmony@../../example/node_modules/react-native-harmony/react_native_openharmony.har"
10
+ },
11
+ "packages": {
12
+ "@ppd/ffrt@1.1.5": {
13
+ "name": "@ppd/ffrt",
14
+ "version": "1.1.5",
15
+ "integrity": "sha512-MbXdRqeNHQQv4ALTGdosvOCQI+xH8nt0+E2/aFLS4UuGJ5541MAeenRCDOrgIG05k7SnSJ4WngsCfjj3FbTCQg==",
16
+ "resolved": "https://ohpm.openharmony.cn/ohpm/@ppd/ffrt/-/ffrt-1.1.5.har",
17
+ "registryType": "ohpm"
18
+ },
19
+ "@rnoh/react-native-openharmony@../../example/node_modules/react-native-harmony/react_native_openharmony.har": {
20
+ "name": "@rnoh/react-native-openharmony",
21
+ "version": "0.77.71",
22
+ "resolved": "../../example/node_modules/react-native-harmony/react_native_openharmony.har",
23
+ "registryType": "local",
24
+ "dependencies": {
25
+ "@ppd/ffrt": "1.1.5"
26
+ }
27
+ }
28
+ }
29
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  name: '@react-native-ohos/react-native-reanimated',
3
- version: '3.18.2-rc.2',
3
+ version: '3.18.3-beta.2',
4
4
  description: "",
5
5
  type: 'module',
6
6
  main: 'index.ets',
@@ -17,6 +17,11 @@ class PropsRegistry {
17
17
  std::lock_guard<std::mutex> createLock() const;
18
18
  // returns a lock you need to hold when calling any of the methods below
19
19
 
20
+ void clear() {
21
+ map_.clear();
22
+ removableShadowNodes_.clear();
23
+ }
24
+
20
25
  void update(const ShadowNode::Shared &shadowNode, folly::dynamic &&props);
21
26
 
22
27
  void for_each(std::function<void(
@@ -105,11 +105,12 @@ void ReanimatedModule::installTurboModule(facebook::jsi::Runtime &rt)
105
105
  self->keyboardEventDataUpdater_ = std::move(keyboardEventDataUpdater);
106
106
  auto weakExecutor = std::weak_ptr(self->m_ctx.taskExecutor);
107
107
  if (auto taskExecutor = weakExecutor.lock()) {
108
- taskExecutor->runTask(TaskThread::MAIN, [ctx = self->m_ctx, isStatusBarTranslucent]() {
108
+ taskExecutor->runTask(TaskThread::MAIN, [ctx = self->m_ctx, isStatusBarTranslucent, isNavigationBarTranslucent]() {
109
109
  ArkJS arkJs(ctx.env);
110
- // arkJs.createBoolean(isStatusBarTranslucent);
110
+ auto isStatusBarTranslucentValue = arkJs.createBoolean(isStatusBarTranslucent);
111
+ auto isNavigationBarTranslucentValue = arkJs.createBoolean(isNavigationBarTranslucent);
111
112
  auto napiTurboModuleObject = arkJs.getObject(ctx.arkTSTurboModuleInstanceRef);
112
- napiTurboModuleObject.call("subscribeKeyBordListeners", {});
113
+ napiTurboModuleObject.call("subscribeKeyBordListeners", {isStatusBarTranslucentValue, isNavigationBarTranslucentValue});
113
114
  });
114
115
  }
115
116
  return 0;
@@ -234,6 +234,28 @@ ReanimatedModuleProxy::~ReanimatedModuleProxy() {
234
234
  frameCallbacks_.clear();
235
235
  #ifdef RCT_NEW_ARCH_ENABLED
236
236
  operationsInBatch_.clear();
237
+ // 清空 PropsRegistry 中持有的 ShadowNode 引用,避免析构时级联释放已被框架回收的 ShadowNodeFamily
238
+ if (propsRegistry_) {
239
+ auto lock = propsRegistry_->createLock();
240
+ // 清空内部 map,释放所有 ShadowNode::Shared
241
+ propsRegistry_->clear();
242
+ }
243
+ propsRegistry_.reset();
244
+
245
+ // LayoutAnimationsProxy 持有 ShadowView/MutationNode 引用(内部包含
246
+ // ShadowNode::Shared),需要在 runtime 销毁前清空,避免 runtime 析构时
247
+ // GC 级联触发 ShadowNodeFamily::~ShadowNodeFamily() 崩溃
248
+ if (layoutAnimationsProxy_) {
249
+ auto lock = std::unique_lock<std::recursive_mutex>(
250
+ layoutAnimationsProxy_->mutex);
251
+ layoutAnimationsProxy_->layoutAnimations_.clear();
252
+ layoutAnimationsProxy_->nodeForTag_.clear();
253
+ layoutAnimationsProxy_->deadNodes.clear();
254
+ layoutAnimationsProxy_->leastRemoved.clear();
255
+ layoutAnimationsProxy_->surfaceManager.props_.clear();
256
+ }
257
+ layoutAnimationsProxy_.reset();
258
+
237
259
  #endif // RCT_NEW_ARCH_ENABLED
238
260
  uiWorkletRuntime_.reset();
239
261
  }
@@ -3,40 +3,104 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import { RNOHLogger, Tag, UITurboModule, UITurboModuleContext } from "@rnoh/react-native-openharmony/ts";
6
- import { window } from '@kit.ArkUI';
7
- import { AnimationManager } from './AnimationManager'
6
+ import { Animator, AnimatorResult, window } from '@kit.ArkUI';
7
+ import { KeyboardState } from "./AnimationManager";
8
8
 
9
9
  export class ReanimatedModule extends UITurboModule {
10
10
  static NAME = "ReanimatedModule"
11
11
  private logger: RNOHLogger
12
12
  private keyBordHeight = 0;
13
+ private currentAnimator: AnimatorResult ;
14
+ private currentAnimatedHeight = 0;
13
15
 
14
- constructor(ctx: UITurboModuleContext) {
16
+ constructor(ctx: UITurboModuleContext) {
15
17
  super(ctx);
16
18
  this.logger = ctx.logger.clone("Reanimated");
17
19
  }
20
+
18
21
  public setViewProps(tag: Tag, props: Object) {
19
22
  this.ctx.descriptorRegistry.setAnimatedRawProps(tag, props);
20
23
  }
21
24
 
22
- public async subscribeKeyBordListeners() {
25
+ public async subscribeKeyBordListeners(isStatusBarTranslucent: boolean, isNavigationBarTranslucent: boolean) {
26
+ //ignore isStatusBarTranslucent - those are Android only
23
27
  const windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
28
+ const navIndicatorArea = windowInstance.getWindowAvoidArea(
29
+ window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
30
+ );
31
+ const navIndicatorHeight = this.ctx.getUIContext().px2vp(
32
+ navIndicatorArea.bottomRect.height
33
+ );
34
+
35
+ const isEdgeToEdge = this.isEdgeToEdgeMode(windowInstance);
36
+ const effectiveNavBarTranslucent = isEdgeToEdge && isNavigationBarTranslucent;
37
+
24
38
  windowInstance.on('keyboardHeightChange', async (height) => {
25
- let keyBordHeight = this.ctx.getUIContext().px2vp(height);
26
- const animationManager = new AnimationManager();
27
- animationManager.setUpdateCallback((value, status) => {
39
+ let rawKeyboardHeight = this.ctx.getUIContext().px2vp(height);
40
+ //If the immersive mode is not enabled, then the area around the bottom navigation bar that needs to be avoided needs to be removed.
41
+ //If the immersive mode is enabled, the returned value will be the height of the current keyboard.
42
+ let keyBordHeight = effectiveNavBarTranslucent
43
+ ? rawKeyboardHeight
44
+ : Math.max(0, rawKeyboardHeight - navIndicatorHeight);
45
+
46
+ //Unchanged height, skip handle
47
+ if (Math.abs(keyBordHeight - this.currentAnimatedHeight) < 0.01) {
48
+ return;
49
+ }
50
+
51
+ const isOpening = keyBordHeight > this.keyBordHeight;
52
+
53
+ if (this.currentAnimator) {
54
+ this.currentAnimator.onFrame = null;
55
+ this.currentAnimator.onFinish = null;
56
+ this.currentAnimator.cancel();
57
+ this.currentAnimator = null;
58
+ }
59
+
60
+ this.currentAnimator = Animator.create({
61
+ duration: isOpening ? 480 : 350,
62
+ delay: 0,
63
+ //simulate the keyboard pop-up and retraction animations in harmony
64
+ easing: isOpening ? "cubic-bezier(0.2, 0.0, 0.1, 1.0)"
65
+ : "cubic-bezier(0.0, 0.0, 0.1, 1.0)",
66
+ fill: "forwards",
67
+ direction: "normal",
68
+ iterations: 1,
69
+ begin: this.currentAnimatedHeight,
70
+ end: keyBordHeight
71
+ }
72
+ )
73
+ this.currentAnimator.onFrame = (progress: number) => {
74
+ this.currentAnimatedHeight = progress;
75
+ if (Math.abs(progress - keyBordHeight) < 0.01) { //The final instruction is sent by "onFinish".
76
+ return;
77
+ }
78
+ let status = keyBordHeight > this.keyBordHeight ? KeyboardState.OPENING : KeyboardState.CLOSING;
28
79
  this.ctx.rnInstance.postMessageToCpp("REANIMATED_KEY_BOARD_CHANGE",
29
- { keyboardHeight: value, status: status.valueOf() });
30
- });
31
- animationManager.startAnimation(this.keyBordHeight, keyBordHeight, 200);
32
- this.keyBordHeight = keyBordHeight;
80
+ { keyboardHeight: progress, status: status.valueOf() });
81
+ }
82
+ this.currentAnimator.onFinish = () => {
83
+ this.currentAnimatedHeight = keyBordHeight;
84
+ let status = keyBordHeight > this.keyBordHeight ? KeyboardState.OPEN : KeyboardState.CLOSED;
85
+ this.keyBordHeight = keyBordHeight;
86
+ this.ctx.rnInstance.postMessageToCpp("REANIMATED_KEY_BOARD_CHANGE",
87
+ { keyboardHeight: keyBordHeight, status: status.valueOf() });
88
+ }
89
+ this.currentAnimator.play();
33
90
  })
34
91
  }
35
92
 
36
93
  public async unsubscribeKeyBordListeners() {
37
94
  const windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
38
- windowInstance.off('keyboardHeightChange', (v) => {
39
- })
95
+ windowInstance.off('keyboardHeightChange')
96
+ if (this.currentAnimator) {
97
+ this.currentAnimator.onFrame = null;
98
+ this.currentAnimator.onFinish = null;
99
+ this.currentAnimator.cancel();
100
+ this.currentAnimator = null;
101
+ }
102
+ this.keyBordHeight = 0;
103
+ this.currentAnimatedHeight = 0;
40
104
  }
41
105
 
42
106
  public setGestureHandlerState(handlerTag: Tag, newState: number) {
@@ -45,4 +109,15 @@ export class ReanimatedModule extends UITurboModule {
45
109
  module.setGestureHandlerState(handlerTag, newState);
46
110
  }
47
111
  }
112
+
113
+ private isEdgeToEdgeMode(windowInstance: window.Window) {
114
+ let isLayoutFullScreen = false;
115
+ try {
116
+ const windowProperties = windowInstance.getWindowProperties();
117
+ isLayoutFullScreen = windowProperties.isLayoutFullScreen;
118
+ } catch (e: ESObject) {
119
+ isLayoutFullScreen = false;
120
+ }
121
+ return isLayoutFullScreen;
122
+ }
48
123
  }
Binary file
package/package.json CHANGED
@@ -4,11 +4,12 @@
4
4
  "alias": "react-native-reanimated",
5
5
  "redirectInternalImports": true
6
6
  },
7
- "version": "3.18.2-rc.2",
7
+ "version": "3.18.3-beta.2",
8
8
  "description": "",
9
9
  "main": "lib/index.js",
10
10
  "scripts": {
11
11
  "build": "tsc",
12
+ "pack":"npm pack",
12
13
  "pack:prod": "npm pack",
13
14
  "prepack": "npm run build",
14
15
  "prepublishOnly": "npm run prepack && react-native verify-package-harmony --package-path . --skip-checks oh_package_name_is_derived_from_npm_package_name",