已关闭
fix: 补充close/open链卸载守卫与兜底;panresponderThreshold阈值生效;supportedOrientations朝向约束 #32
mazheng创建于 8月31日关闭于 9月17日
fix: 补充close/open链卸载守卫与兜底;panresponderThreshold阈值生效;supportedOrientations朝向约束 #32
已关闭
共 6 个文件变更+333-98
| @@ -1,3 +1,12 @@ | |||
| 1 | +# v14.0.2-beta.2 | ||
| 2 | + | ||
| 3 | +- fix: 修复关闭弹窗时抛出 TypeError 的问题。为 open/close 链路补充组件卸载守卫(卸载后置空 ref、丢弃迟到的动画回调),并在 animatable 宿主缺失时走无动画兜底,避免 `isTransitioning` 卡在 true 后再也无法打开/关闭弹窗。 | ||
| 4 | +- fix: `panResponderThreshold` 属性生效。滑动识别改为在累计位移超过阈值后才触发,低于阈值的手势视为点击,弹窗不再位移,`onSwipeStart`/`onSwipeMove` 也不会误触发。 | ||
| 5 | +- fix: `hideModalContentWhileAnimating` 属性生效。原逻辑依赖 `useNativeDriver` 且依赖弹窗可见期恒为 true 的 `showContent`,导致属性始终不起作用;现改为由进出场过渡自身驱动,取值为 true 时整个动画过程都隐藏弹窗内容,且不再受动画驱动方式限制。 | ||
| 6 | +- feat: HarmonyOS 上按 `supportedOrientations` 约束弹窗布局尺寸,窗口旋转到未声明支持的方向时,弹窗仍按支持的方向渲染(iOS 原生能力的布局层模拟,不会真正锁定窗口方向)。 | ||
| 7 | +- fix: 滑动百分比改用真实窗口尺寸计算,修复朝向约束下分母为负、导致遮罩透明度异常与 `onSwipeMove` 回传百分比越界的问题。 | ||
| 8 | +- docs: 更新中英文文档中 `hideModalContentWhileAnimating`、`panResponderThreshold` 的 HarmonyOS 平台支持情况,并同步移除对应的遗留问题描述。 | ||
| 9 | + | ||
| 1 | # v14.0.2-beta.1 | 10 | # v14.0.2-beta.1 |
| 2 | 11 | ||
| 3 | - fix: 解决 77 框架上获取版本号空指针问题,添加非空处理 | 12 | - fix: 解决 77 框架上获取版本号空指针问题,添加非空处理 |
| @@ -181,7 +181,7 @@ export default DefaultModal; | |||
| 181 | | onSwipeMove | `func` | (percentageShown) => null | no | all | yes | 每次滑动事件触发时调用 | | 181 | | onSwipeMove | `func` | (percentageShown) => null | no | all | yes | 每次滑动事件触发时调用 | |
| 182 | | onSwipeComplete | `func` | ({ swipingDirection }) => null | no | all | yes | 达到滑动阈值时触发 | | 182 | | onSwipeComplete | `func` | ({ swipingDirection }) => null | no | all | yes | 达到滑动阈值时触发 | |
| 183 | | onSwipeCancel | `func` | () => null | no | all | yes | 未达到滑动阈值时触发 | | 183 | | onSwipeCancel | `func` | () => null | no | all | yes | 未达到滑动阈值时触发 | |
| 184 | -| panResponderThreshold | `number` | 4 | no | all | no | 触摸响应器开始识别滑动事件的阈值 | | 184 | +| panResponderThreshold | `number` | 4 | no | all | yes | 触摸响应器开始识别滑动事件的阈值 | |
| 185 | | scrollOffset | `number` | 0 | no | all | no | 大于 0 时禁用滑动关闭功能,用于实现可滚动内容 | | 185 | | scrollOffset | `number` | 0 | no | all | no | 大于 0 时禁用滑动关闭功能,用于实现可滚动内容 | |
| 186 | | scrollOffsetMax | `number` | 0 | no | all | yes | 用于实现内容可滚动时的越界滚动效果 | | 186 | | scrollOffsetMax | `number` | 0 | no | all | yes | 用于实现内容可滚动时的越界滚动效果 | |
| 187 | | scrollTo | `func` | null | no | all | yes | 用于实现可滚动弹窗 | | 187 | | scrollTo | `func` | null | no | all | yes | 用于实现可滚动弹窗 | |
| @@ -190,7 +190,7 @@ export default DefaultModal; | |||
| 190 | | swipeDirection | `string` or `array` | null | no | all | yes | 定义弹窗可滑动的方向,支持 up/down/left/right 单个值或数组组合 | | 190 | | swipeDirection | `string` or `array` | null | no | all | yes | 定义弹窗可滑动的方向,支持 up/down/left/right 单个值或数组组合 | |
| 191 | | useNativeDriver | `bool` | false | no | all | yes | 是否使用原生驱动执行动画 | | 191 | | useNativeDriver | `bool` | false | no | all | yes | 是否使用原生驱动执行动画 | |
| 192 | | useNativeDriverForBackdrop | `bool` | null | no | all | yes | 遮罩层动画是否使用原生驱动(避免安卓端闪烁问题) | | 192 | | useNativeDriverForBackdrop | `bool` | null | no | all | yes | 遮罩层动画是否使用原生驱动(避免安卓端闪烁问题) | |
| 193 | -| hideModalContentWhileAnimating | `bool` | false | no | all | no | 动画完成前隐藏弹窗内容以提升性能 | | 193 | +| hideModalContentWhileAnimating | `bool` | false | no | all | yes | 动画完成前隐藏弹窗内容以提升性能 | |
| 194 | | propagateSwipe | `bool` or `func` | false | no | all | no | 允许滑动事件传递给子组件(如弹窗内的滚动视图) | | 194 | | propagateSwipe | `bool` or `func` | false | no | all | no | 允许滑动事件传递给子组件(如弹窗内的滚动视图) | |
| 195 | | style | `any` | null | no | all | yes | 应用到弹窗的样式 | | 195 | | style | `any` | null | no | all | yes | 应用到弹窗的样式 | |
| 196 | | hardwareAccelerated | `bool` | false | no | android | no | 强制为 Modal 独立窗口开启 GPU 硬件加速渲染 | | 196 | | hardwareAccelerated | `bool` | false | no | android | no | 强制为 Modal 独立窗口开启 GPU 硬件加速渲染 | |
| @@ -210,8 +210,7 @@ export default DefaultModal; | |||
| 210 | 210 | ||
| 211 | ## 遗留问题 | 211 | ## 遗留问题 |
| 212 | 212 | ||
| 213 | -- [ ] hideModalContentWhileAnimating 属性无效果,上游社区 bug。 | 213 | +- [ ] 三端表现一致,存在滑动冲突问题,在此场景下不支持 scrollOffset,propagateSwipe。 |
| 214 | -- [ ] 三端表现一致,存在滑动冲突问题,在此场景下不支持 scrollOffset,propagateSwipe,panResponderThreshold。 | ||
| 215 | - [ ] 组件显示及关闭时框架或 ArkUI 的 modal 的 isVisible 状态切换与动画的结束时机存在时序竞争,导致视觉呈现 Modal 闪烁。 | 214 | - [ ] 组件显示及关闭时框架或 ArkUI 的 modal 的 isVisible 状态切换与动画的结束时机存在时序竞争,导致视觉呈现 Modal 闪烁。 |
| 216 | 215 | ||
| 217 | ## 其他 | 216 | ## 其他 |
| @@ -181,7 +181,7 @@ export default DefaultModal; | |||
| 181 | | onSwipeMove | `func` | (percentageShown) => null | no | all | yes | Called on each swipe event | | 181 | | onSwipeMove | `func` | (percentageShown) => null | no | all | yes | Called on each swipe event | |
| 182 | | onSwipeComplete | `func` | ({ swipingDirection }) => null | no | all | yes | Triggered when the swipe threshold is reached | | 182 | | onSwipeComplete | `func` | ({ swipingDirection }) => null | no | all | yes | Triggered when the swipe threshold is reached | |
| 183 | | onSwipeCancel | `func` | () => null | no | all | yes | Triggered when the swipe threshold is not reached | | 183 | | onSwipeCancel | `func` | () => null | no | all | yes | Triggered when the swipe threshold is not reached | |
| 184 | -| panResponderThreshold | `number` | 4 | no | all | no | Threshold at which the touch responder starts recognizing swipe events | | 184 | +| panResponderThreshold | `number` | 4 | no | all | yes | Threshold at which the touch responder starts recognizing swipe events | |
| 185 | | scrollOffset | `number` | 0 | no | all | no | When greater than 0, disables swipe-to-close; used for scrollable content | | 185 | | scrollOffset | `number` | 0 | no | all | no | When greater than 0, disables swipe-to-close; used for scrollable content | |
| 186 | | scrollOffsetMax | `number` | 0 | no | all | yes | Used to implement overscroll effects when content is scrollable | | 186 | | scrollOffsetMax | `number` | 0 | no | all | yes | Used to implement overscroll effects when content is scrollable | |
| 187 | | scrollTo | `func` | null | no | all | yes | Used to implement a scrollable modal | | 187 | | scrollTo | `func` | null | no | all | yes | Used to implement a scrollable modal | |
| @@ -190,7 +190,7 @@ export default DefaultModal; | |||
| 190 | | swipeDirection | `string` or `array` | null | no | all | yes | Defines the swipe direction(s) of the modal; supports a single value or an array combination of up/down/left/right | | 190 | | swipeDirection | `string` or `array` | null | no | all | yes | Defines the swipe direction(s) of the modal; supports a single value or an array combination of up/down/left/right | |
| 191 | | useNativeDriver | `bool` | false | no | all | yes | Whether to use the native driver to run animations | | 191 | | useNativeDriver | `bool` | false | no | all | yes | Whether to use the native driver to run animations | |
| 192 | | useNativeDriverForBackdrop | `bool` | null | no | all | yes | Whether the backdrop animation uses the native driver (avoids flicker on Android) | | 192 | | useNativeDriverForBackdrop | `bool` | null | no | all | yes | Whether the backdrop animation uses the native driver (avoids flicker on Android) | |
| 193 | -| hideModalContentWhileAnimating | `bool` | false | no | all | no | Hide the modal content before the animation completes to improve performance | | 193 | +| hideModalContentWhileAnimating | `bool` | false | no | all | yes | Hide the modal content before the animation completes to improve performance | |
| 194 | | propagateSwipe | `bool` or `func` | false | no | all | no | Allow swipe events to propagate to child components (e.g., a scroll view inside the modal) | | 194 | | propagateSwipe | `bool` or `func` | false | no | all | no | Allow swipe events to propagate to child components (e.g., a scroll view inside the modal) | |
| 195 | | style | `any` | null | no | all | yes | Style applied to the modal | | 195 | | style | `any` | null | no | all | yes | Style applied to the modal | |
| 196 | | hardwareAccelerated | `bool` | false | no | android | no | Force GPU hardware-accelerated rendering for the Modal's independent window | | 196 | | hardwareAccelerated | `bool` | false | no | android | no | Force GPU hardware-accelerated rendering for the Modal's independent window | |
| @@ -210,8 +210,7 @@ export default DefaultModal; | |||
| 210 | 210 | ||
| 211 | ## Known Issues | 211 | ## Known Issues |
| 212 | 212 | ||
| 213 | -- [ ] The hideModalContentWhileAnimating property has no effect; it’s an upstream community bug. | 213 | +- [ ] Consistent behavior across all three platforms. There are sliding conflict issues, and scrollOffset and propagateSwipe are not supported in this scenario. |
| 214 | -- [ ] Consistent behavior across all three platforms. There are sliding conflict issues, and scrollOffset, propagateSwipe, and panResponderThreshold are not supported in this scenario. | ||
| 215 | - [ ] When the component shows and dismisses, a timing race condition exists between the `isVisible` state toggle of framework/ArkUI Modal and animation finish timing, which causes Modal visual flickering. | 214 | - [ ] When the component shows and dismisses, a timing race condition exists between the `isVisible` state toggle of framework/ArkUI Modal and animation finish timing, which causes Modal visual flickering. |
| 216 | 215 | ||
| 217 | ## Others | 216 | ## Others |
| @@ -8,7 +8,7 @@ | |||
| 8 | "start": "hdc rport tcp:8081 tcp:8081 && react-native start", | 8 | "start": "hdc rport tcp:8081 tcp:8081 && react-native start", |
| 9 | "codegen": "react-native codegen-harmony --rnoh-module-path ./harmony/entry/oh_modules/@rnoh/react-native-openharmony", | 9 | "codegen": "react-native codegen-harmony --rnoh-module-path ./harmony/entry/oh_modules/@rnoh/react-native-openharmony", |
| 10 | "pack:pkg": "cd ../ && npm pack && cd ./example", | 10 | "pack:pkg": "cd ../ && npm pack && cd ./example", |
| 11 | - "install:pkg": "npm uninstall @react-native-ohos/react-native-modal && npm run pack:pkg && npm i @react-native-ohos/react-native-modal@file:../react-native-ohos-react-native-modal-14.0.2-beta.1.tgz", | 11 | + "install:pkg": "npm uninstall @react-native-ohos/react-native-modal && npm run pack:pkg && npm i @react-native-ohos/react-native-modal@file:../react-native-ohos-react-native-modal-14.0.2-beta.2.tgz", |
| 12 | "dev": "npm run codegen && react-native bundle-harmony --dev --minify=false", | 12 | "dev": "npm run codegen && react-native bundle-harmony --dev --minify=false", |
| 13 | "prod": "npm run codegen && react-native bundle-harmony --dev=false --minify=true", | 13 | "prod": "npm run codegen && react-native bundle-harmony --dev=false --minify=true", |
| 14 | "postinstall": "node ./scripts/create-build-profile", | 14 | "postinstall": "node ./scripts/create-build-profile", |
| @@ -17,7 +17,7 @@ | |||
| 17 | }, | 17 | }, |
| 18 | "dependencies": { | 18 | "dependencies": { |
| 19 | "@react-native-oh/react-native-harmony": "0.77.18-1", | 19 | "@react-native-oh/react-native-harmony": "0.77.18-1", |
| 20 | - "@react-native-ohos/react-native-modal": "file:../react-native-ohos-react-native-modal-14.0.2-beta.1.tgz", | 20 | + "@react-native-ohos/react-native-modal": "file:../react-native-ohos-react-native-modal-14.0.2-beta.2.tgz", |
| 21 | "react": "18.3.1", | 21 | "react": "18.3.1", |
| 22 | "react-native": "0.77.1", | 22 | "react-native": "0.77.1", |
| 23 | "react-native-harmony": "npm:@react-native-oh/react-native-harmony@0.77.18-1" | 23 | "react-native-harmony": "npm:@react-native-oh/react-native-harmony@0.77.18-1" |
| @@ -1,6 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "@react-native-ohos/react-native-modal", | 2 | "name": "@react-native-ohos/react-native-modal", |
| 3 | - "version": "14.0.2-beta.1", | 3 | + "version": "14.0.2-beta.2", |
| 4 | "description": "An enhanced React Native modal", | 4 | "description": "An enhanced React Native modal", |
| 5 | "main": "dist/index.js", | 5 | "main": "dist/index.js", |
| 6 | "types": "dist/index.d.ts", | 6 | "types": "dist/index.d.ts", |
| @@ -47,9 +47,19 @@ export type OnSwipeCompleteParams = { | |||
| 47 | 47 | ||
| 48 | type State = { | 48 | type State = { |
| 49 | showContent: boolean; | 49 | showContent: boolean; |
| 50 | + // True from the moment an entrance/exit transition is kicked off until it | ||
| 51 | + // settles. Unlike the isTransitioning instance field this lives in state, | ||
| 52 | + // so toggling it re-renders and hideModalContentWhileAnimating can actually | ||
| 53 | + // take effect. | ||
| 54 | + isAnimatingContent: boolean; | ||
| 50 | isVisible: boolean; | 55 | isVisible: boolean; |
| 51 | deviceWidth: number; | 56 | deviceWidth: number; |
| 52 | deviceHeight: number; | 57 | deviceHeight: number; |
| 58 | + // Real (unconstrained) window size. The backdrop is sized from the real | ||
| 59 | + // window at render time, so these are tracked to detect rotations that do | ||
| 60 | + // not change the orientation-constrained geometry and still re-render. | ||
| 61 | + realWindowWidth: number; | ||
| 62 | + realWindowHeight: number; | ||
| 53 | isSwipeable: boolean; | 63 | isSwipeable: boolean; |
| 54 | pan: OrNull<Animated.ValueXY>; | 64 | pan: OrNull<Animated.ValueXY>; |
| 55 | }; | 65 | }; |