@mpxjs/webpack-plugin 2.10.6 → 2.10.7-beta.10

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.
Files changed (51) hide show
  1. package/lib/dependencies/RecordPageConfigsMapDependency.js +1 -1
  2. package/lib/index.js +71 -51
  3. package/lib/parser.js +1 -1
  4. package/lib/platform/json/wx/index.js +0 -1
  5. package/lib/platform/style/wx/index.js +7 -0
  6. package/lib/platform/template/wx/component-config/button.js +1 -1
  7. package/lib/platform/template/wx/component-config/index.js +5 -1
  8. package/lib/platform/template/wx/component-config/input.js +1 -1
  9. package/lib/platform/template/wx/component-config/movable-view.js +1 -10
  10. package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
  11. package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
  12. package/lib/react/processJSON.js +2 -1
  13. package/lib/runtime/components/react/AsyncContainer.tsx +189 -0
  14. package/lib/runtime/components/react/context.ts +23 -4
  15. package/lib/runtime/components/react/dist/AsyncContainer.jsx +141 -0
  16. package/lib/runtime/components/react/dist/context.js +5 -2
  17. package/lib/runtime/components/react/dist/mpx-button.jsx +2 -2
  18. package/lib/runtime/components/react/dist/mpx-input.jsx +1 -1
  19. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +64 -10
  20. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +358 -98
  21. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +3 -0
  22. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +31 -15
  23. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
  24. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  25. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +2 -2
  26. package/lib/runtime/components/react/dist/mpx-swiper.jsx +53 -27
  27. package/lib/runtime/components/react/dist/mpx-view.jsx +21 -7
  28. package/lib/runtime/components/react/dist/mpx-web-view.jsx +13 -13
  29. package/lib/runtime/components/react/dist/utils.jsx +94 -1
  30. package/lib/runtime/components/react/mpx-button.tsx +3 -2
  31. package/lib/runtime/components/react/mpx-input.tsx +1 -1
  32. package/lib/runtime/components/react/mpx-movable-area.tsx +99 -12
  33. package/lib/runtime/components/react/mpx-movable-view.tsx +413 -100
  34. package/lib/runtime/components/react/mpx-rich-text/index.tsx +3 -0
  35. package/lib/runtime/components/react/mpx-scroll-view.tsx +84 -59
  36. package/lib/runtime/components/react/mpx-sticky-header.tsx +181 -0
  37. package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
  38. package/lib/runtime/components/react/mpx-swiper-item.tsx +2 -2
  39. package/lib/runtime/components/react/mpx-swiper.tsx +53 -25
  40. package/lib/runtime/components/react/mpx-view.tsx +20 -7
  41. package/lib/runtime/components/react/mpx-web-view.tsx +12 -12
  42. package/lib/runtime/components/react/utils.tsx +93 -1
  43. package/lib/runtime/components/web/mpx-scroll-view.vue +18 -4
  44. package/lib/runtime/components/web/mpx-sticky-header.vue +99 -0
  45. package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
  46. package/lib/runtime/optionProcessor.js +0 -2
  47. package/lib/script-setup-compiler/index.js +27 -5
  48. package/lib/template-compiler/bind-this.js +2 -1
  49. package/lib/template-compiler/compiler.js +4 -3
  50. package/package.json +4 -4
  51. package/LICENSE +0 -433
@@ -32,6 +32,7 @@
32
32
  * ✔ bindscroll
33
33
  */
34
34
  import { ScrollView, RefreshControl, Gesture, GestureDetector } from 'react-native-gesture-handler';
35
+ import { Animated as RNAnimated } from 'react-native';
35
36
  import { isValidElement, Children, useRef, useState, useEffect, forwardRef, useContext, useMemo, createElement } from 'react';
36
37
  import Animated, { useAnimatedRef, useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated';
37
38
  import { warn, hasOwn } from '@mpxjs/utils';
@@ -40,9 +41,11 @@ import useNodesRef from './useNodesRef';
40
41
  import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, HIDDEN_STYLE } from './utils';
41
42
  import { IntersectionObserverContext, ScrollViewContext } from './context';
42
43
  import Portal from './mpx-portal';
44
+ const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
43
45
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
44
46
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
45
- const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'refresher-threshold': refresherThreshold = 45, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
47
+ const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'refresher-threshold': refresherThreshold = 45, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'enable-sticky': enableSticky, 'scroll-event-throttle': scrollEventThrottle = 0, 'scroll-into-view-offset': scrollIntoViewOffset = 0, __selectRef } = props;
48
+ const scrollOffset = useRef(new RNAnimated.Value(0)).current;
46
49
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
47
50
  const waitForHandlers = flatGesture(waitFor);
48
51
  const snapScrollTop = useRef(0);
@@ -66,7 +69,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
66
69
  const hasCallScrollToLower = useRef(false);
67
70
  const initialTimeout = useRef(null);
68
71
  const intersectionObservers = useContext(IntersectionObserverContext);
69
- const firstScrollIntoViewChange = useRef(false);
72
+ const firstScrollIntoViewChange = useRef(true);
70
73
  const refreshColor = {
71
74
  black: ['#000'],
72
75
  white: ['#fff']
@@ -86,16 +89,18 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
86
89
  pagingEnabled,
87
90
  fastDeceleration: false,
88
91
  decelerationDisabled: false,
89
- scrollTo
92
+ scrollTo,
93
+ scrollIntoView: handleScrollIntoView
90
94
  },
91
95
  gestureRef: scrollViewRef
92
96
  });
97
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
93
98
  const contextValue = useMemo(() => {
94
99
  return {
95
- gestureRef: scrollViewRef
100
+ gestureRef: scrollViewRef,
101
+ scrollOffset
96
102
  };
97
103
  }, []);
98
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
99
104
  const hasRefresherLayoutRef = useRef(false);
100
105
  // layout 完成前先隐藏,避免安卓闪烁问题
101
106
  const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
@@ -115,14 +120,16 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
115
120
  }, [scrollTop, scrollLeft]);
116
121
  useEffect(() => {
117
122
  if (scrollIntoView && __selectRef) {
118
- if (!firstScrollIntoViewChange.current) {
119
- setTimeout(handleScrollIntoView);
123
+ if (firstScrollIntoViewChange.current) {
124
+ setTimeout(() => {
125
+ handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation });
126
+ });
120
127
  }
121
128
  else {
122
- handleScrollIntoView();
129
+ handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation });
123
130
  }
124
131
  }
125
- firstScrollIntoViewChange.current = true;
132
+ firstScrollIntoViewChange.current = false;
126
133
  }, [scrollIntoView]);
127
134
  useEffect(() => {
128
135
  if (refresherEnabled) {
@@ -142,13 +149,15 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
142
149
  function scrollTo({ top = 0, left = 0, animated = false }) {
143
150
  scrollToOffset(left, top, animated);
144
151
  }
145
- function handleScrollIntoView() {
146
- const refs = __selectRef(`#${scrollIntoView}`, 'node');
152
+ function handleScrollIntoView(selector = '', { offset = 0, animated = true } = {}) {
153
+ const refs = __selectRef(`#${selector}`, 'node');
147
154
  if (!refs)
148
155
  return;
149
156
  const { nodeRef } = refs.getNodeInstance();
150
157
  nodeRef.current?.measureLayout(scrollViewRef.current, (left, top) => {
151
- scrollToOffset(left, top);
158
+ const adjustedLeft = scrollX ? left + offset : left;
159
+ const adjustedTop = scrollY ? top + offset : top;
160
+ scrollToOffset(adjustedLeft, adjustedTop, animated);
152
161
  });
153
162
  }
154
163
  function selectLength(size) {
@@ -321,6 +330,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
321
330
  updateScrollOptions(e, { scrollLeft, scrollTop });
322
331
  updateIntersection();
323
332
  }
333
+ const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
334
+ useNativeDriver: true,
335
+ listener: (event) => {
336
+ onScroll(event);
337
+ }
338
+ });
324
339
  function onScrollDragStart(e) {
325
340
  hasCallScrollToLower.current = false;
326
341
  hasCallScrollToUpper.current = false;
@@ -481,7 +496,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
481
496
  scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
482
497
  bounces: false,
483
498
  ref: scrollViewRef,
484
- onScroll: onScroll,
499
+ onScroll: enableSticky ? scrollHandler : onScroll,
485
500
  onContentSizeChange: onContentSizeChange,
486
501
  bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
487
502
  bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
@@ -523,13 +538,14 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
523
538
  'bindscrolltolower',
524
539
  'bindrefresherrefresh'
525
540
  ], { layoutRef });
526
- const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollView, innerProps, createElement(Animated.View, { style: [refresherAnimatedStyle, refresherLayoutStyle], onLayout: onRefresherLayout }, refresherContent), createElement(Animated.View, { style: contentAnimatedStyle }, createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(extendObject({}, props, { children: otherContent }), {
541
+ const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
542
+ const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollViewComponent, innerProps, createElement(Animated.View, { style: [refresherAnimatedStyle, refresherLayoutStyle], onLayout: onRefresherLayout }, refresherContent), createElement(Animated.View, { style: contentAnimatedStyle }, createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(extendObject({}, props, { children: otherContent }), {
527
543
  hasVarDec,
528
544
  varContext: varContextRef.current,
529
545
  textStyle,
530
546
  textProps
531
547
  })))));
532
- const commonScrollView = createElement(ScrollView, extendObject(innerProps, {
548
+ const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
533
549
  refreshControl: refresherEnabled
534
550
  ? createElement(RefreshControl, extendObject({
535
551
  progressBackgroundColor: refresherBackground,
@@ -0,0 +1,117 @@
1
+ import { useEffect, useRef, useContext, forwardRef, useMemo, createElement, useId } from 'react';
2
+ import { Animated, StyleSheet, useAnimatedValue } from 'react-native';
3
+ import { ScrollViewContext, StickyContext } from './context';
4
+ import useNodesRef from './useNodesRef';
5
+ import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
6
+ import { error } from '@mpxjs/utils';
7
+ import useInnerProps, { getCustomEvent } from './getInnerListeners';
8
+ const _StickyHeader = forwardRef((stickyHeaderProps = {}, ref) => {
9
+ const { textProps, innerProps: props = {} } = splitProps(stickyHeaderProps);
10
+ const { style, bindstickontopchange, padding = [0, 0, 0, 0], 'offset-top': offsetTop = 0, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
11
+ const scrollViewContext = useContext(ScrollViewContext);
12
+ const stickyContext = useContext(StickyContext);
13
+ const { scrollOffset } = scrollViewContext;
14
+ const { registerStickyHeader, unregisterStickyHeader } = stickyContext;
15
+ const headerRef = useRef(null);
16
+ const isStickOnTopRef = useRef(false);
17
+ const id = useId();
18
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
19
+ const { layoutRef, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: headerRef, onLayout });
20
+ const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
21
+ const headerTopAnimated = useAnimatedValue(0);
22
+ // harmony animatedValue 不支持通过 _value 访问
23
+ const headerTopRef = useRef(0);
24
+ useEffect(() => {
25
+ registerStickyHeader({ key: id, updatePosition });
26
+ return () => {
27
+ unregisterStickyHeader(id);
28
+ };
29
+ }, []);
30
+ function updatePosition() {
31
+ if (headerRef.current) {
32
+ const scrollViewRef = scrollViewContext.gestureRef;
33
+ if (scrollViewRef && scrollViewRef.current) {
34
+ headerRef.current.measureLayout(scrollViewRef.current, (left, top) => {
35
+ Animated.timing(headerTopAnimated, {
36
+ toValue: top,
37
+ duration: 0,
38
+ useNativeDriver: true
39
+ }).start();
40
+ headerTopRef.current = top;
41
+ });
42
+ }
43
+ else {
44
+ error('StickyHeader measureLayout error: scrollViewRef is not a valid native component reference');
45
+ }
46
+ }
47
+ }
48
+ function onLayout(e) {
49
+ updatePosition();
50
+ }
51
+ useNodesRef(props, ref, headerRef, {
52
+ style: normalStyle
53
+ });
54
+ useEffect(() => {
55
+ if (!bindstickontopchange)
56
+ return;
57
+ const listener = scrollOffset.addListener((state) => {
58
+ const currentScrollValue = state.value;
59
+ const newIsStickOnTop = currentScrollValue > headerTopRef.current;
60
+ if (newIsStickOnTop !== isStickOnTopRef.current) {
61
+ isStickOnTopRef.current = newIsStickOnTop;
62
+ bindstickontopchange(getCustomEvent('stickontopchange', {}, {
63
+ detail: {
64
+ isStickOnTop: newIsStickOnTop
65
+ },
66
+ layoutRef
67
+ }, props));
68
+ }
69
+ });
70
+ return () => {
71
+ scrollOffset.removeListener(listener);
72
+ };
73
+ }, []);
74
+ const animatedStyle = useMemo(() => {
75
+ const translateY = Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
76
+ inputRange: [0, 1],
77
+ outputRange: [0, 1],
78
+ extrapolateLeft: 'clamp',
79
+ extrapolateRight: 'extend'
80
+ });
81
+ const finalTranslateY = offsetTop === 0
82
+ ? translateY
83
+ : Animated.add(translateY, Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
84
+ inputRange: [0, 1],
85
+ outputRange: [0, offsetTop],
86
+ extrapolate: 'clamp'
87
+ }));
88
+ return {
89
+ transform: [{ translateY: finalTranslateY }]
90
+ };
91
+ }, [scrollOffset, headerTopAnimated, offsetTop]);
92
+ const innerProps = useInnerProps(extendObject({}, props, {
93
+ ref: headerRef,
94
+ style: extendObject({}, styles.content, innerStyle, animatedStyle, {
95
+ paddingTop: padding[0] || 0,
96
+ paddingRight: padding[1] || 0,
97
+ paddingBottom: padding[2] || 0,
98
+ paddingLeft: padding[3] || 0
99
+ })
100
+ }, layoutProps), [], { layoutRef });
101
+ return (createElement(Animated.View, innerProps, wrapChildren(props, {
102
+ hasVarDec,
103
+ varContext: varContextRef.current,
104
+ textStyle,
105
+ textProps
106
+ })));
107
+ });
108
+ const styles = StyleSheet.create({
109
+ content: {
110
+ width: '100%',
111
+ zIndex: 10,
112
+ // harmony 需要手动设置 relative, zIndex 才生效
113
+ position: 'relative'
114
+ }
115
+ });
116
+ _StickyHeader.displayName = 'MpxStickyHeader';
117
+ export default _StickyHeader;
@@ -0,0 +1,45 @@
1
+ import { useRef, forwardRef, createElement, useCallback, useMemo } from 'react';
2
+ import { View } from 'react-native';
3
+ import useNodesRef from './useNodesRef';
4
+ import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
5
+ import { StickyContext } from './context';
6
+ import useInnerProps from './getInnerListeners';
7
+ const _StickySection = forwardRef((stickySectionProps = {}, ref) => {
8
+ const { textProps, innerProps: props = {} } = splitProps(stickySectionProps);
9
+ const { style, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
10
+ const sectionRef = useRef(null);
11
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
12
+ const { layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: sectionRef, onLayout });
13
+ const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
14
+ const stickyHeaders = useRef(new Map());
15
+ const registerStickyHeader = useCallback((item) => {
16
+ stickyHeaders.current.set(item.id, item);
17
+ }, []);
18
+ const unregisterStickyHeader = useCallback((id) => {
19
+ stickyHeaders.current.delete(id);
20
+ }, []);
21
+ const contextValue = useMemo(() => ({
22
+ registerStickyHeader,
23
+ unregisterStickyHeader
24
+ }), []);
25
+ useNodesRef(props, ref, sectionRef, {
26
+ style: normalStyle
27
+ });
28
+ function onLayout() {
29
+ stickyHeaders.current.forEach(item => {
30
+ item.updatePosition();
31
+ });
32
+ }
33
+ const innerProps = useInnerProps(extendObject({}, props, {
34
+ style: extendObject(innerStyle, layoutStyle),
35
+ ref: sectionRef
36
+ }, layoutProps), [], { layoutRef });
37
+ return (createElement(View, innerProps, createElement(StickyContext.Provider, { value: contextValue }, wrapChildren(props, {
38
+ hasVarDec,
39
+ varContext: varContextRef.current,
40
+ textStyle,
41
+ textProps
42
+ }))));
43
+ });
44
+ _StickySection.displayName = 'MpxStickySection';
45
+ export default _StickySection;
@@ -2,7 +2,7 @@ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated
2
2
  import { forwardRef, useRef, useContext, createElement } from 'react';
3
3
  import useInnerProps from './getInnerListeners';
4
4
  import useNodesRef from './useNodesRef'; // 引入辅助函数
5
- import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject } from './utils';
5
+ import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject, isHarmony } from './utils';
6
6
  import { SwiperContext } from './context';
7
7
  const _SwiperItem = forwardRef((props, ref) => {
8
8
  const { 'enable-var': enableVar, 'external-var-context': externalVarContext, style, customStyle, itemIndex } = props;
@@ -29,7 +29,7 @@ const _SwiperItem = forwardRef((props, ref) => {
29
29
  'style'
30
30
  ], { layoutRef });
31
31
  const itemAnimatedStyle = useAnimatedStyle(() => {
32
- if (!step.value)
32
+ if (!step.value && !isHarmony)
33
33
  return {};
34
34
  const inputRange = [step.value, 0];
35
35
  const outputRange = [0.7, 1];
@@ -125,6 +125,10 @@ const SwiperWrapper = forwardRef((props, ref) => {
125
125
  const moveTranstion = useSharedValue(0);
126
126
  // 记录从onBegin 到 onTouchesUp 的时间
127
127
  const moveTime = useSharedValue(0);
128
+ // 记录从onBegin 到 onTouchesCancelled 另外一个方向移动的距离
129
+ const anotherDirectionMove = useSharedValue(0);
130
+ // 另一个方向的
131
+ const anotherAbso = 'absolute' + (dir === 'x' ? 'y' : 'x').toUpperCase();
128
132
  const timerId = useRef(0);
129
133
  const intervalTimer = props.interval || 500;
130
134
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
@@ -405,7 +409,11 @@ const SwiperWrapper = forwardRef((props, ref) => {
405
409
  }
406
410
  }, [children.length]);
407
411
  useEffect(() => {
408
- updateCurrent(props.current || 0, step.value);
412
+ // 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
413
+ // 2. 手指滑动过程中更新索引,外部会把current再穿进来,导致offset直接更新了
414
+ if (props.current !== currentIndex.value) {
415
+ updateCurrent(props.current || 0, step.value);
416
+ }
409
417
  }, [props.current]);
410
418
  useEffect(() => {
411
419
  autoplayShared.value = autoplay;
@@ -468,20 +476,26 @@ const SwiperWrapper = forwardRef((props, ref) => {
468
476
  targetOffset: -moveToTargetPos
469
477
  };
470
478
  }
471
- function canMove(eventData) {
479
+ function checkUnCircular(eventData) {
472
480
  'worklet';
473
481
  const { translation } = eventData;
474
482
  const currentOffset = Math.abs(offset.value);
475
- if (!circularShared.value) {
476
- if (translation < 0) {
477
- return currentOffset < step.value * (childrenLength.value - 1);
478
- }
479
- else {
480
- return currentOffset > 0;
481
- }
483
+ // 向右滑动swiper
484
+ if (translation < 0) {
485
+ const boundaryOffset = step.value * (childrenLength.value - 1);
486
+ const gestureMovePos = Math.abs(translation) + currentOffset;
487
+ return {
488
+ // 防止快速连续向右滑动时,手势移动的距离 当前的offset超出边界
489
+ targetOffset: gestureMovePos > boundaryOffset ? -boundaryOffset : offset.value + translation,
490
+ canMove: currentOffset < boundaryOffset
491
+ };
482
492
  }
483
493
  else {
484
- return true;
494
+ const gestureMovePos = currentOffset - translation;
495
+ return {
496
+ targetOffset: gestureMovePos < 0 ? 0 : offset.value + translation,
497
+ canMove: currentOffset > 0
498
+ };
485
499
  }
486
500
  }
487
501
  function handleEnd(eventData) {
@@ -541,7 +555,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
541
555
  }
542
556
  });
543
557
  }
544
- function handleLongPress() {
558
+ function computeHalf() {
545
559
  'worklet';
546
560
  const currentOffset = Math.abs(offset.value);
547
561
  let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value;
@@ -551,6 +565,14 @@ const SwiperWrapper = forwardRef((props, ref) => {
551
565
  // 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
552
566
  const diffOffset = preOffset - currentOffset;
553
567
  const half = Math.abs(diffOffset) > step.value / 2;
568
+ return {
569
+ diffOffset,
570
+ half
571
+ };
572
+ }
573
+ function handleLongPress() {
574
+ 'worklet';
575
+ const { diffOffset, half } = computeHalf();
554
576
  if (+diffOffset === 0) {
555
577
  runOnJS(resumeLoop)();
556
578
  }
@@ -610,19 +632,30 @@ const SwiperWrapper = forwardRef((props, ref) => {
610
632
  runOnJS(pauseLoop)();
611
633
  preAbsolutePos.value = e[strAbso];
612
634
  moveTranstion.value = e[strAbso];
635
+ anotherDirectionMove.value = e[anotherAbso];
613
636
  moveTime.value = new Date().getTime();
614
637
  })
615
- .onTouchesMove((e) => {
638
+ .onUpdate((e) => {
616
639
  'worklet';
617
640
  if (touchfinish.value)
618
641
  return;
619
- const touchEventData = e.changedTouches[0];
620
- const moveDistance = touchEventData[strAbso] - preAbsolutePos.value;
642
+ const moveDistance = e[strAbso] - preAbsolutePos.value;
621
643
  const eventData = {
622
644
  translation: moveDistance
623
645
  };
624
- // 处理用户一直拖拽到临界点的场景, 不会执行onEnd
625
- if (!circularShared.value && !canMove(eventData)) {
646
+ // 1. 在Move过程中,如果手指一直没抬起来,超过一半的话也会更新索引
647
+ const { half } = computeHalf();
648
+ if (half) {
649
+ const { selectedIndex } = getTargetPosition(eventData);
650
+ currentIndex.value = selectedIndex;
651
+ }
652
+ // 2. 处理用户一直拖拽到临界点的场景, 不会执行onEnd
653
+ const { canMove, targetOffset } = checkUnCircular(eventData);
654
+ if (!circularShared.value) {
655
+ if (canMove) {
656
+ offset.value = targetOffset;
657
+ preAbsolutePos.value = e[strAbso];
658
+ }
626
659
  return;
627
660
  }
628
661
  const { isBoundary, resetOffset } = reachBoundary(eventData);
@@ -632,30 +665,23 @@ const SwiperWrapper = forwardRef((props, ref) => {
632
665
  else {
633
666
  offset.value = moveDistance + offset.value;
634
667
  }
635
- preAbsolutePos.value = touchEventData[strAbso];
668
+ preAbsolutePos.value = e[strAbso];
636
669
  })
637
- .onTouchesUp((e) => {
670
+ .onFinalize((e) => {
638
671
  'worklet';
639
672
  if (touchfinish.value)
640
673
  return;
641
- const touchEventData = e.changedTouches[0];
642
- const moveDistance = touchEventData[strAbso] - moveTranstion.value;
674
+ const moveDistance = e[strAbso] - moveTranstion.value;
643
675
  touchfinish.value = true;
644
676
  const eventData = {
645
677
  translation: moveDistance
646
678
  };
647
- if (childrenLength.value === 1) {
648
- return handleBackInit();
649
- }
650
- // 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
651
- if (!circularShared.value && !canMove(eventData)) {
652
- return;
653
- }
654
679
  const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000;
655
680
  if (Math.abs(strVelocity) < longPressRatio) {
656
681
  handleLongPress();
657
682
  }
658
683
  else {
684
+ // 如果触发了onTouchesCancelled,不会触发onUpdate不会更新offset值, 索引不会变更
659
685
  handleEnd(eventData);
660
686
  }
661
687
  })
@@ -57,7 +57,7 @@ const normalizeStyle = (style = {}) => {
57
57
  const isPercent = (val) => typeof val === 'string' && PERCENT_REGEX.test(val);
58
58
  const isBackgroundSizeKeyword = (val) => typeof val === 'string' && /^cover|contain$/.test(val);
59
59
  const isNeedLayout = (preImageInfo) => {
60
- const { sizeList, backgroundPosition, linearInfo } = preImageInfo;
60
+ const { sizeList, backgroundPosition, linearInfo, type } = preImageInfo;
61
61
  const [width, height] = sizeList;
62
62
  const bp = backgroundPosition;
63
63
  // 含有百分号,center 需计算布局
@@ -66,7 +66,8 @@ const isNeedLayout = (preImageInfo) => {
66
66
  (isPercent(width) && height === 'auto') ||
67
67
  isPercent(bp[1]) ||
68
68
  isPercent(bp[3]) ||
69
- isDiagonalAngle(linearInfo);
69
+ isDiagonalAngle(linearInfo) ||
70
+ (type === 'linear' && (isPercent(height) || isPercent(width)));
70
71
  };
71
72
  const checkNeedLayout = (preImageInfo) => {
72
73
  const { sizeList } = preImageInfo;
@@ -155,7 +156,7 @@ function backgroundPosition(imageProps, preImageInfo, imageSize, layoutInfo) {
155
156
  }
156
157
  // background-size 转换
157
158
  function backgroundSize(imageProps, preImageInfo, imageSize, layoutInfo) {
158
- const sizeList = preImageInfo.sizeList;
159
+ const { sizeList, type } = preImageInfo;
159
160
  if (!sizeList)
160
161
  return;
161
162
  const { width: layoutWidth, height: layoutHeight } = layoutInfo || {};
@@ -202,10 +203,23 @@ function backgroundSize(imageProps, preImageInfo, imageSize, layoutInfo) {
202
203
  else { // 数值类型 ImageStyle
203
204
  // 数值类型设置为 stretch
204
205
  imageProps.resizeMode = 'stretch';
205
- dimensions = {
206
- width: isPercent(width) ? width : +width,
207
- height: isPercent(height) ? height : +height
208
- };
206
+ if (type === 'linear') {
207
+ const dimensionWidth = calcPercent(width, layoutWidth) || 0;
208
+ const dimensionHeight = calcPercent(height, layoutHeight) || 0;
209
+ // ios 上 linear 组件只要重新触发渲染,在渲染过程中 width 或者 height 被设置为 0,即使后面再更新为正常宽高,也会渲染不出来
210
+ if (dimensionWidth && dimensionHeight) {
211
+ dimensions = {
212
+ width: dimensionWidth,
213
+ height: dimensionHeight
214
+ };
215
+ }
216
+ }
217
+ else {
218
+ dimensions = {
219
+ width: isPercent(width) ? width : +width,
220
+ height: isPercent(height) ? height : +height
221
+ };
222
+ }
209
223
  }
210
224
  }
211
225
  // 样式合并
@@ -1,4 +1,4 @@
1
- import { forwardRef, useRef, useContext, useMemo, useState, useEffect } from 'react';
1
+ import { forwardRef, useRef, useContext, useMemo, useState } from 'react';
2
2
  import { warn, isFunction } from '@mpxjs/utils';
3
3
  import Portal from './mpx-portal/index';
4
4
  import { getCustomEvent } from './getInnerListeners';
@@ -74,17 +74,17 @@ const _WebView = forwardRef((props, ref) => {
74
74
  isNavigateBack.current = false;
75
75
  };
76
76
  const navigation = useNavigation();
77
- useEffect(() => {
78
- let beforeRemoveSubscription;
79
- if (__mpx_mode__ !== 'ios') {
80
- beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle);
81
- }
82
- return () => {
83
- if (isFunction(beforeRemoveSubscription)) {
84
- beforeRemoveSubscription();
85
- }
86
- };
87
- }, []);
77
+ // useEffect(() => {
78
+ // let beforeRemoveSubscription:any
79
+ // if (__mpx_mode__ !== 'ios') {
80
+ // beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle)
81
+ // }
82
+ // return () => {
83
+ // if (isFunction(beforeRemoveSubscription)) {
84
+ // beforeRemoveSubscription()
85
+ // }
86
+ // }
87
+ // }, [])
88
88
  useNodesRef(props, ref, webViewRef, {
89
89
  style: defaultWebViewStyle
90
90
  });
@@ -160,7 +160,7 @@ const _WebView = forwardRef((props, ref) => {
160
160
  { // case下不允许直接声明,包个块解决该问题
161
161
  const title = postData._documentTitle?.trim();
162
162
  if (title !== undefined) {
163
- navigation && navigation.setOptions({ title });
163
+ navigation && navigation.setPageConfig({ navigationBarTitleText: title });
164
164
  }
165
165
  }
166
166
  break;