@mpxjs/webpack-plugin 2.10.7-beta.9 → 2.10.8-beta.1
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/lib/dependencies/RequireExternalDependency.js +61 -0
- package/lib/file-loader.js +3 -2
- package/lib/index.js +60 -15
- package/lib/json-compiler/index.js +1 -0
- package/lib/parser.js +1 -1
- package/lib/platform/json/wx/index.js +43 -25
- package/lib/platform/template/wx/component-config/fix-component-name.js +2 -2
- package/lib/platform/template/wx/component-config/index.js +2 -0
- package/lib/platform/template/wx/component-config/movable-view.js +10 -1
- package/lib/platform/template/wx/component-config/page-container.js +19 -0
- package/lib/platform/template/wx/component-config/unsupported.js +1 -1
- package/lib/platform/template/wx/index.js +2 -1
- package/lib/react/LoadAsyncChunkModule.js +74 -0
- package/lib/react/index.js +3 -1
- package/lib/react/processJSON.js +74 -13
- package/lib/react/processScript.js +6 -6
- package/lib/react/script-helper.js +100 -41
- package/lib/runtime/components/react/context.ts +2 -12
- package/lib/runtime/components/react/dist/context.js +1 -1
- package/lib/runtime/components/react/dist/getInnerListeners.js +1 -1
- package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +135 -0
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +9 -63
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +58 -301
- package/lib/runtime/components/react/dist/mpx-page-container.jsx +255 -0
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +30 -55
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +14 -28
- package/lib/runtime/components/react/dist/useAnimationHooks.js +2 -87
- package/lib/runtime/components/react/getInnerListeners.ts +1 -1
- package/lib/runtime/components/react/mpx-async-suspense.tsx +180 -0
- package/lib/runtime/components/react/mpx-movable-area.tsx +11 -98
- package/lib/runtime/components/react/mpx-movable-view.tsx +60 -350
- package/lib/runtime/components/react/mpx-page-container.tsx +394 -0
- package/lib/runtime/components/react/mpx-swiper.tsx +29 -55
- package/lib/runtime/components/react/mpx-web-view.tsx +13 -33
- package/lib/runtime/components/react/types/global.d.ts +15 -0
- package/lib/runtime/components/react/useAnimationHooks.ts +2 -85
- package/lib/runtime/optionProcessorReact.d.ts +18 -0
- package/lib/runtime/optionProcessorReact.js +30 -0
- package/lib/script-setup-compiler/index.js +27 -5
- package/lib/template-compiler/compiler.js +27 -6
- package/lib/utils/dom-tag-config.js +18 -4
- package/lib/utils/trans-async-sub-rules.js +19 -0
- package/lib/web/script-helper.js +1 -1
- package/package.json +3 -3
- package/lib/runtime/components/react/AsyncContainer.tsx +0 -189
- package/lib/runtime/components/react/dist/AsyncContainer.jsx +0 -141
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { createElement, forwardRef, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { StyleSheet, Animated, Dimensions, TouchableWithoutFeedback, PanResponder } from 'react-native';
|
|
3
|
+
import Portal from './mpx-portal/index';
|
|
4
|
+
import { usePreventRemove } from '@react-navigation/native';
|
|
5
|
+
import { extendObject, useLayout, useNavigation } from './utils';
|
|
6
|
+
import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
7
|
+
import useNodesRef from './useNodesRef';
|
|
8
|
+
const screenHeight = Dimensions.get('window').height;
|
|
9
|
+
const screenWidth = Dimensions.get('window').width;
|
|
10
|
+
function nextTick(cb) {
|
|
11
|
+
setTimeout(cb, 0);
|
|
12
|
+
}
|
|
13
|
+
export default forwardRef((props, ref) => {
|
|
14
|
+
const { show, duration = 300, 'z-index': zIndex = 100, overlay = true, position = 'bottom', round = false, 'close-on-slide-down': closeOnSlideDown = false, 'overlay-style': overlayStyle, 'custom-style': customStyle, bindclose, // RN下特有属性,用于同步show状态到父组件
|
|
15
|
+
bindbeforeenter, bindenter, bindafterenter, bindbeforeleave, bindleave, bindafterleave, bindclickoverlay, children } = props;
|
|
16
|
+
const isFirstRenderFlag = useRef(true);
|
|
17
|
+
const isFirstRender = isFirstRenderFlag.current;
|
|
18
|
+
if (isFirstRenderFlag.current) {
|
|
19
|
+
isFirstRenderFlag.current = false;
|
|
20
|
+
}
|
|
21
|
+
const close = () => {
|
|
22
|
+
bindclose(getCustomEvent('close', {}, { detail: { value: false, source: 'close' } }, props));
|
|
23
|
+
};
|
|
24
|
+
const [internalVisible, setInternalVisible] = useState(show); // 控制组件是否挂载
|
|
25
|
+
const overlayOpacity = useRef(new Animated.Value(0)).current;
|
|
26
|
+
const contentOpacity = useRef(new Animated.Value(position === 'center' ? 0 : 1)).current;
|
|
27
|
+
const contentTranslate = useRef(new Animated.Value(getInitialPosition())).current;
|
|
28
|
+
const currentAnimation = useRef(null);
|
|
29
|
+
function getInitialPosition() {
|
|
30
|
+
switch (position) {
|
|
31
|
+
case 'top':
|
|
32
|
+
return -screenHeight;
|
|
33
|
+
case 'bottom':
|
|
34
|
+
return screenHeight;
|
|
35
|
+
case 'right':
|
|
36
|
+
return screenWidth;
|
|
37
|
+
case 'center':
|
|
38
|
+
return 0;
|
|
39
|
+
default:
|
|
40
|
+
return screenHeight;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const currentTick = useRef(0);
|
|
44
|
+
function createTick() {
|
|
45
|
+
currentTick.current++;
|
|
46
|
+
console.log('currentTick.current++', currentTick.current);
|
|
47
|
+
const current = currentTick.current;
|
|
48
|
+
return () => {
|
|
49
|
+
console.log('currentTick.current', currentTick.current, 'current', current);
|
|
50
|
+
return currentTick.current === current;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// 播放入场动画
|
|
54
|
+
const animateIn = () => {
|
|
55
|
+
const isCurrentTick = createTick();
|
|
56
|
+
const animateOutFinish = currentAnimation.current === null;
|
|
57
|
+
if (!animateOutFinish) {
|
|
58
|
+
currentAnimation.current.forEach((animation) => animation.stop());
|
|
59
|
+
}
|
|
60
|
+
const animations = [
|
|
61
|
+
Animated.timing(contentTranslate, {
|
|
62
|
+
toValue: 0,
|
|
63
|
+
duration,
|
|
64
|
+
useNativeDriver: true
|
|
65
|
+
}),
|
|
66
|
+
Animated.timing(contentOpacity, {
|
|
67
|
+
toValue: 1,
|
|
68
|
+
duration,
|
|
69
|
+
useNativeDriver: true
|
|
70
|
+
}),
|
|
71
|
+
Animated.timing(overlayOpacity, {
|
|
72
|
+
toValue: 1,
|
|
73
|
+
duration,
|
|
74
|
+
useNativeDriver: true
|
|
75
|
+
})
|
|
76
|
+
];
|
|
77
|
+
currentAnimation.current = animations;
|
|
78
|
+
// 所有生命周期需相隔一个nextTick以保证在生命周期中修改show可在组件内部监听到
|
|
79
|
+
bindbeforeenter && bindbeforeenter(getCustomEvent('beforeenter', {}, { detail: { value: false, source: 'beforeenter' } }, props));
|
|
80
|
+
nextTick(() => {
|
|
81
|
+
bindenter && bindenter(getCustomEvent('enter', {}, { detail: { value: false, source: 'enter' } }, props));
|
|
82
|
+
// 与微信对其, bindenter 需要执行,所以 isCurrentTick 放在后面
|
|
83
|
+
if (!isCurrentTick())
|
|
84
|
+
return;
|
|
85
|
+
console.log('animateIn start');
|
|
86
|
+
// 设置为动画初始状态(特殊情况, 如果退场动画没有结束 或者 退场动画还未开始,则无需初始化,而是从当前位置完成动画)
|
|
87
|
+
if (animateOutFinish) {
|
|
88
|
+
contentTranslate.setValue(getInitialPosition());
|
|
89
|
+
contentOpacity.setValue(position === 'center' ? 0 : 1);
|
|
90
|
+
}
|
|
91
|
+
Animated.parallel(animations).start(() => {
|
|
92
|
+
bindafterenter && bindafterenter(getCustomEvent('afterenter', {}, { detail: { value: false, source: 'afterenter' } }, props));
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
// 播放离场动画
|
|
97
|
+
const animateOut = () => {
|
|
98
|
+
const isCurrentTick = createTick();
|
|
99
|
+
// 停止入场动画
|
|
100
|
+
currentAnimation.current?.forEach((animation) => animation.stop());
|
|
101
|
+
const animations = [Animated.timing(overlayOpacity, {
|
|
102
|
+
toValue: 0,
|
|
103
|
+
duration,
|
|
104
|
+
useNativeDriver: true
|
|
105
|
+
})
|
|
106
|
+
];
|
|
107
|
+
if (position === 'center') {
|
|
108
|
+
animations.push(Animated.timing(contentOpacity, {
|
|
109
|
+
toValue: 0,
|
|
110
|
+
duration,
|
|
111
|
+
useNativeDriver: true
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
animations.push(Animated.timing(contentTranslate, {
|
|
116
|
+
toValue: getInitialPosition(),
|
|
117
|
+
duration,
|
|
118
|
+
useNativeDriver: true
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
currentAnimation.current = animations;
|
|
122
|
+
bindbeforeleave && bindbeforeleave(getCustomEvent('beforeleave', {}, { detail: { value: false, source: 'beforeleave' } }, props));
|
|
123
|
+
nextTick(() => {
|
|
124
|
+
bindleave && bindleave(getCustomEvent('leave', {}, { detail: { value: false, source: 'leave' } }, props));
|
|
125
|
+
if (!isCurrentTick())
|
|
126
|
+
return;
|
|
127
|
+
console.log('animateOut start');
|
|
128
|
+
Animated.parallel(animations).start(() => {
|
|
129
|
+
currentAnimation.current = null;
|
|
130
|
+
bindafterleave && bindafterleave(getCustomEvent('afterleave', {}, { detail: { value: false, source: 'afterleave' } }, props));
|
|
131
|
+
setInternalVisible(false); // 动画播放完后,才卸载
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
console.log('====comp show', show, 'internalVisible', internalVisible);
|
|
137
|
+
// 如果展示状态和挂载状态一致,则不需要做任何操作
|
|
138
|
+
if (show) {
|
|
139
|
+
setInternalVisible(true); // 确保挂载
|
|
140
|
+
animateIn();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
if (!isFirstRender)
|
|
144
|
+
animateOut();
|
|
145
|
+
}
|
|
146
|
+
}, [show]);
|
|
147
|
+
const navigation = useNavigation();
|
|
148
|
+
usePreventRemove(show, (event) => {
|
|
149
|
+
const { data } = event;
|
|
150
|
+
if (show) {
|
|
151
|
+
close();
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
navigation?.dispatch(data.action);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
// IOS 下需要关闭手势返回(原因: IOS手势返回时页面会跟随手指滑动,但是实际返回动作是在松手时触发,需禁掉页面跟随手指滑动的效果)
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
navigation?.setOptions({
|
|
160
|
+
gestureEnabled: !show
|
|
161
|
+
});
|
|
162
|
+
}, [show]);
|
|
163
|
+
const SCREEN_EDGE_THRESHOLD = 60; // 从屏幕左侧 30px 内触发
|
|
164
|
+
// 内容区 手势下滑关闭
|
|
165
|
+
const contentPanResponder = PanResponder.create({
|
|
166
|
+
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
167
|
+
const { dx, dy } = gestureState;
|
|
168
|
+
return dy > 200 && Math.abs(dx) < 60;
|
|
169
|
+
},
|
|
170
|
+
onPanResponderRelease: () => {
|
|
171
|
+
close();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
// 全屏幕 IOS 右滑手势返回
|
|
175
|
+
const screenPanResponder = PanResponder.create({
|
|
176
|
+
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
177
|
+
const { moveX, dx, dy } = gestureState;
|
|
178
|
+
const isFromEdge = moveX < SCREEN_EDGE_THRESHOLD;
|
|
179
|
+
const isHorizontalSwipe = dx > 10 && Math.abs(dy) < 20;
|
|
180
|
+
return isFromEdge && isHorizontalSwipe;
|
|
181
|
+
},
|
|
182
|
+
onPanResponderRelease: (_, gestureState) => {
|
|
183
|
+
if (gestureState.dx > 100) {
|
|
184
|
+
close();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
const getTransformStyle = () => {
|
|
189
|
+
switch (position) {
|
|
190
|
+
case 'top':
|
|
191
|
+
case 'bottom':
|
|
192
|
+
return { transform: [{ translateY: contentTranslate }] };
|
|
193
|
+
case 'right':
|
|
194
|
+
return { transform: [{ translateX: contentTranslate }] };
|
|
195
|
+
case 'center':
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const renderMask = () => {
|
|
200
|
+
const onPress = () => {
|
|
201
|
+
close();
|
|
202
|
+
bindclickoverlay && bindclickoverlay(getCustomEvent('clickoverlay', {}, { detail: { value: false, source: 'clickoverlay' } }, props));
|
|
203
|
+
};
|
|
204
|
+
return createElement(TouchableWithoutFeedback, { onPress }, createElement(Animated.View, { style: [styles.overlay, overlayStyle, { opacity: overlayOpacity }] }));
|
|
205
|
+
};
|
|
206
|
+
const renderContent = (children) => {
|
|
207
|
+
const contentProps = extendObject({
|
|
208
|
+
style: [
|
|
209
|
+
styles.container,
|
|
210
|
+
round ? styles.rounded : null,
|
|
211
|
+
positionStyle[position],
|
|
212
|
+
customStyle,
|
|
213
|
+
getTransformStyle(),
|
|
214
|
+
{ opacity: contentOpacity }
|
|
215
|
+
]
|
|
216
|
+
}, closeOnSlideDown ? contentPanResponder.panHandlers : null);
|
|
217
|
+
return createElement(Animated.View, contentProps, children);
|
|
218
|
+
};
|
|
219
|
+
const nodeRef = useRef(null);
|
|
220
|
+
useNodesRef(props, ref, nodeRef, {});
|
|
221
|
+
const { layoutRef, layoutProps } = useLayout({ props, hasSelfPercent: false, nodeRef });
|
|
222
|
+
const innerProps = useInnerProps(extendObject({}, props, {
|
|
223
|
+
ref: nodeRef
|
|
224
|
+
}, layoutProps), [], { layoutRef });
|
|
225
|
+
const wrapperProps = extendObject(innerProps, {
|
|
226
|
+
style: [styles.wrapper, { zIndex }]
|
|
227
|
+
}, __mpx_mode__ === 'ios' ? screenPanResponder.panHandlers : {});
|
|
228
|
+
// TODO 是否有必要支持refs? dataset?
|
|
229
|
+
return createElement(Portal, null, internalVisible
|
|
230
|
+
? createElement(Animated.View, wrapperProps, overlay ? renderMask() : null, renderContent(children))
|
|
231
|
+
: null);
|
|
232
|
+
});
|
|
233
|
+
const styles = StyleSheet.create({
|
|
234
|
+
wrapper: extendObject({
|
|
235
|
+
justifyContent: 'flex-end',
|
|
236
|
+
alignItems: 'center'
|
|
237
|
+
}, StyleSheet.absoluteFillObject),
|
|
238
|
+
overlay: extendObject({
|
|
239
|
+
backgroundColor: 'rgba(0,0,0,0.5)'
|
|
240
|
+
}, StyleSheet.absoluteFillObject),
|
|
241
|
+
container: {
|
|
242
|
+
position: 'absolute',
|
|
243
|
+
backgroundColor: 'white'
|
|
244
|
+
},
|
|
245
|
+
rounded: {
|
|
246
|
+
borderTopLeftRadius: 20,
|
|
247
|
+
borderTopRightRadius: 20
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const positionStyle = {
|
|
251
|
+
bottom: { bottom: 0, width: '100%', height: 'auto' },
|
|
252
|
+
top: { top: 0, width: '100%', height: 'auto' },
|
|
253
|
+
right: extendObject({}, StyleSheet.absoluteFillObject, { right: 0 }),
|
|
254
|
+
center: extendObject({}, StyleSheet.absoluteFillObject)
|
|
255
|
+
};
|
|
@@ -71,7 +71,7 @@ const easeMap = {
|
|
|
71
71
|
easeInOutCubic: Easing.inOut(Easing.cubic)
|
|
72
72
|
};
|
|
73
73
|
const SwiperWrapper = forwardRef((props, ref) => {
|
|
74
|
-
const { 'indicator-dots': showPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, autoplay = false, circular = false, disableGesture = false } = props;
|
|
74
|
+
const { 'indicator-dots': showPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, autoplay = false, circular = false, disableGesture = false, bindchange } = props;
|
|
75
75
|
const easeingFunc = props['easing-function'] || 'default';
|
|
76
76
|
const easeDuration = props.duration || 500;
|
|
77
77
|
const horizontal = props.vertical !== undefined ? !props.vertical : true;
|
|
@@ -125,10 +125,6 @@ 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();
|
|
132
128
|
const timerId = useRef(0);
|
|
133
129
|
const intervalTimer = props.interval || 500;
|
|
134
130
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
@@ -334,6 +330,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
334
330
|
if (props.current !== currentIndex.value) {
|
|
335
331
|
const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef });
|
|
336
332
|
props.bindchange && props.bindchange(eventData);
|
|
333
|
+
bindchange && bindchange(eventData);
|
|
337
334
|
}
|
|
338
335
|
}
|
|
339
336
|
function getOffset(index, stepValue) {
|
|
@@ -377,7 +374,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
377
374
|
// 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
|
|
378
375
|
useAnimatedReaction(() => currentIndex.value, (newIndex, preIndex) => {
|
|
379
376
|
// 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
|
|
380
|
-
if (newIndex !== preIndex &&
|
|
377
|
+
if (newIndex !== preIndex && bindchange) {
|
|
381
378
|
runOnJS(handleSwiperChange)(newIndex);
|
|
382
379
|
}
|
|
383
380
|
});
|
|
@@ -409,11 +406,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
409
406
|
}
|
|
410
407
|
}, [children.length]);
|
|
411
408
|
useEffect(() => {
|
|
412
|
-
|
|
413
|
-
// 2. 手指滑动过程中更新索引,外部会把current再穿进来,导致offset直接更新了
|
|
414
|
-
if (props.current !== currentIndex.value) {
|
|
415
|
-
updateCurrent(props.current || 0, step.value);
|
|
416
|
-
}
|
|
409
|
+
updateCurrent(props.current || 0, step.value);
|
|
417
410
|
}, [props.current]);
|
|
418
411
|
useEffect(() => {
|
|
419
412
|
autoplayShared.value = autoplay;
|
|
@@ -476,26 +469,20 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
476
469
|
targetOffset: -moveToTargetPos
|
|
477
470
|
};
|
|
478
471
|
}
|
|
479
|
-
function
|
|
472
|
+
function canMove(eventData) {
|
|
480
473
|
'worklet';
|
|
481
474
|
const { translation } = eventData;
|
|
482
475
|
const currentOffset = Math.abs(offset.value);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
canMove: currentOffset < boundaryOffset
|
|
491
|
-
};
|
|
476
|
+
if (!circularShared.value) {
|
|
477
|
+
if (translation < 0) {
|
|
478
|
+
return currentOffset < step.value * (childrenLength.value - 1);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
return currentOffset > 0;
|
|
482
|
+
}
|
|
492
483
|
}
|
|
493
484
|
else {
|
|
494
|
-
|
|
495
|
-
return {
|
|
496
|
-
targetOffset: gestureMovePos < 0 ? 0 : offset.value + translation,
|
|
497
|
-
canMove: currentOffset > 0
|
|
498
|
-
};
|
|
485
|
+
return true;
|
|
499
486
|
}
|
|
500
487
|
}
|
|
501
488
|
function handleEnd(eventData) {
|
|
@@ -555,7 +542,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
555
542
|
}
|
|
556
543
|
});
|
|
557
544
|
}
|
|
558
|
-
function
|
|
545
|
+
function handleLongPress() {
|
|
559
546
|
'worklet';
|
|
560
547
|
const currentOffset = Math.abs(offset.value);
|
|
561
548
|
let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value;
|
|
@@ -565,14 +552,6 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
565
552
|
// 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
|
|
566
553
|
const diffOffset = preOffset - currentOffset;
|
|
567
554
|
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();
|
|
576
555
|
if (+diffOffset === 0) {
|
|
577
556
|
runOnJS(resumeLoop)();
|
|
578
557
|
}
|
|
@@ -632,30 +611,19 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
632
611
|
runOnJS(pauseLoop)();
|
|
633
612
|
preAbsolutePos.value = e[strAbso];
|
|
634
613
|
moveTranstion.value = e[strAbso];
|
|
635
|
-
anotherDirectionMove.value = e[anotherAbso];
|
|
636
614
|
moveTime.value = new Date().getTime();
|
|
637
615
|
})
|
|
638
|
-
.
|
|
616
|
+
.onTouchesMove((e) => {
|
|
639
617
|
'worklet';
|
|
640
618
|
if (touchfinish.value)
|
|
641
619
|
return;
|
|
642
|
-
const
|
|
620
|
+
const touchEventData = e.changedTouches[0];
|
|
621
|
+
const moveDistance = touchEventData[strAbso] - preAbsolutePos.value;
|
|
643
622
|
const eventData = {
|
|
644
623
|
translation: moveDistance
|
|
645
624
|
};
|
|
646
|
-
//
|
|
647
|
-
|
|
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
|
-
}
|
|
625
|
+
// 处理用户一直拖拽到临界点的场景, 不会执行onEnd
|
|
626
|
+
if (!circularShared.value && !canMove(eventData)) {
|
|
659
627
|
return;
|
|
660
628
|
}
|
|
661
629
|
const { isBoundary, resetOffset } = reachBoundary(eventData);
|
|
@@ -665,23 +633,30 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
665
633
|
else {
|
|
666
634
|
offset.value = moveDistance + offset.value;
|
|
667
635
|
}
|
|
668
|
-
preAbsolutePos.value =
|
|
636
|
+
preAbsolutePos.value = touchEventData[strAbso];
|
|
669
637
|
})
|
|
670
|
-
.
|
|
638
|
+
.onTouchesUp((e) => {
|
|
671
639
|
'worklet';
|
|
672
640
|
if (touchfinish.value)
|
|
673
641
|
return;
|
|
674
|
-
const
|
|
642
|
+
const touchEventData = e.changedTouches[0];
|
|
643
|
+
const moveDistance = touchEventData[strAbso] - moveTranstion.value;
|
|
675
644
|
touchfinish.value = true;
|
|
676
645
|
const eventData = {
|
|
677
646
|
translation: moveDistance
|
|
678
647
|
};
|
|
648
|
+
if (childrenLength.value === 1) {
|
|
649
|
+
return handleBackInit();
|
|
650
|
+
}
|
|
651
|
+
// 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
|
|
652
|
+
if (!circularShared.value && !canMove(eventData)) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
679
655
|
const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000;
|
|
680
656
|
if (Math.abs(strVelocity) < longPressRatio) {
|
|
681
657
|
handleLongPress();
|
|
682
658
|
}
|
|
683
659
|
else {
|
|
684
|
-
// 如果触发了onTouchesCancelled,不会触发onUpdate不会更新offset值, 索引不会变更
|
|
685
660
|
handleEnd(eventData);
|
|
686
661
|
}
|
|
687
662
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
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
|
+
import { usePreventRemove } from '@react-navigation/native';
|
|
4
5
|
import { getCustomEvent } from './getInnerListeners';
|
|
5
6
|
import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy';
|
|
6
7
|
import { WebView } from 'react-native-webview';
|
|
@@ -55,8 +56,8 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
55
56
|
const webViewRef = useRef(null);
|
|
56
57
|
const fristLoaded = useRef(false);
|
|
57
58
|
const isLoadError = useRef(false);
|
|
59
|
+
const isNavigateBack = useRef(false);
|
|
58
60
|
const statusCode = useRef('');
|
|
59
|
-
const [isLoaded, setIsLoaded] = useState(true);
|
|
60
61
|
const defaultWebViewStyle = {
|
|
61
62
|
position: 'absolute',
|
|
62
63
|
left: 0,
|
|
@@ -64,27 +65,18 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
64
65
|
top: 0,
|
|
65
66
|
bottom: 0
|
|
66
67
|
};
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
const navigation = useNavigation();
|
|
69
|
+
const [isIntercept, setIsIntercept] = useState(false);
|
|
70
|
+
usePreventRemove(isIntercept, (event) => {
|
|
71
|
+
const { data } = event;
|
|
72
|
+
if (isNavigateBack.current) {
|
|
73
|
+
navigation?.dispatch(data.action);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
71
76
|
webViewRef.current?.goBack();
|
|
72
|
-
e.preventDefault();
|
|
73
77
|
}
|
|
74
78
|
isNavigateBack.current = false;
|
|
75
|
-
};
|
|
76
|
-
const navigation = useNavigation();
|
|
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
|
-
// }, [])
|
|
79
|
+
});
|
|
88
80
|
useNodesRef(props, ref, webViewRef, {
|
|
89
81
|
style: defaultWebViewStyle
|
|
90
82
|
});
|
|
@@ -131,13 +123,13 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
131
123
|
};
|
|
132
124
|
const _changeUrl = function (navState) {
|
|
133
125
|
if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑
|
|
134
|
-
canGoBack.current = navState.canGoBack;
|
|
135
126
|
currentPage.__webViewUrl = navState.url;
|
|
127
|
+
setIsIntercept(navState.canGoBack);
|
|
136
128
|
}
|
|
137
129
|
};
|
|
138
130
|
const _onLoadProgress = function (event) {
|
|
139
131
|
if (__mpx_mode__ !== 'ios') {
|
|
140
|
-
|
|
132
|
+
setIsIntercept(event.nativeEvent.canGoBack);
|
|
141
133
|
}
|
|
142
134
|
};
|
|
143
135
|
const _message = function (res) {
|
|
@@ -227,7 +219,6 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
227
219
|
};
|
|
228
220
|
const onLoadEndHandle = function (res) {
|
|
229
221
|
fristLoaded.current = true;
|
|
230
|
-
setIsLoaded(true);
|
|
231
222
|
const src = res.nativeEvent?.url;
|
|
232
223
|
if (isLoadError.current) {
|
|
233
224
|
isLoadError.current = false;
|
|
@@ -275,18 +266,13 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
275
266
|
setPageLoadErr(true);
|
|
276
267
|
}
|
|
277
268
|
};
|
|
278
|
-
const onLoadStart = function () {
|
|
279
|
-
if (!fristLoaded.current) {
|
|
280
|
-
setIsLoaded(false);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
269
|
return (<Portal>
|
|
284
270
|
{pageLoadErr
|
|
285
271
|
? (<View style={[styles.loadErrorContext, defaultWebViewStyle]}>
|
|
286
272
|
<View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>
|
|
287
273
|
<View style={styles.loadErrorButton} onTouchEnd={_reload}><Text style={{ fontSize: 12, color: '#666666' }}>{currentErrorText.button}</Text></View>
|
|
288
274
|
</View>)
|
|
289
|
-
: (<WebView style={defaultWebViewStyle}
|
|
275
|
+
: (<WebView style={defaultWebViewStyle} source={{ uri: src }} ref={webViewRef} javaScriptEnabled={true} onNavigationStateChange={_changeUrl} onMessage={_message} injectedJavaScript={injectedJavaScript} onLoadProgress={_onLoadProgress} onLoadEnd={onLoadEnd} onHttpError={onHttpError} onError={onError} allowsBackForwardNavigationGestures={true}></WebView>)}
|
|
290
276
|
</Portal>);
|
|
291
277
|
});
|
|
292
278
|
_WebView.displayName = 'MpxWebview';
|
|
@@ -48,90 +48,6 @@ const InitialValue = Object.assign({
|
|
|
48
48
|
const TransformOrigin = 'transformOrigin';
|
|
49
49
|
// transform
|
|
50
50
|
const isTransform = (key) => Object.keys(TransformInitial).includes(key);
|
|
51
|
-
// 多value解析
|
|
52
|
-
const parseValues = (str, char = ' ') => {
|
|
53
|
-
let stack = 0;
|
|
54
|
-
let temp = '';
|
|
55
|
-
const result = [];
|
|
56
|
-
for (let i = 0; i < str.length; i++) {
|
|
57
|
-
if (str[i] === '(') {
|
|
58
|
-
stack++;
|
|
59
|
-
}
|
|
60
|
-
else if (str[i] === ')') {
|
|
61
|
-
stack--;
|
|
62
|
-
}
|
|
63
|
-
// 非括号内 或者 非分隔字符且非空
|
|
64
|
-
if (stack !== 0 || (str[i] !== char && str[i] !== ' ')) {
|
|
65
|
-
temp += str[i];
|
|
66
|
-
}
|
|
67
|
-
if ((stack === 0 && str[i] === char) || i === str.length - 1) {
|
|
68
|
-
result.push(temp);
|
|
69
|
-
temp = '';
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
73
|
-
};
|
|
74
|
-
// parse string transform, eg: transform: 'rotateX(45deg) rotateZ(0.785398rad)'
|
|
75
|
-
const parseTransform = (transformStr) => {
|
|
76
|
-
const values = parseValues(transformStr);
|
|
77
|
-
const transform = [];
|
|
78
|
-
values.forEach(item => {
|
|
79
|
-
const match = item.match(/([/\w]+)\((.+)\)/);
|
|
80
|
-
if (match && match.length >= 3) {
|
|
81
|
-
let key = match[1];
|
|
82
|
-
const val = match[2];
|
|
83
|
-
switch (key) {
|
|
84
|
-
case 'translateX':
|
|
85
|
-
case 'translateY':
|
|
86
|
-
case 'scaleX':
|
|
87
|
-
case 'scaleY':
|
|
88
|
-
case 'rotateX':
|
|
89
|
-
case 'rotateY':
|
|
90
|
-
case 'rotateZ':
|
|
91
|
-
case 'rotate':
|
|
92
|
-
case 'skewX':
|
|
93
|
-
case 'skewY':
|
|
94
|
-
case 'perspective':
|
|
95
|
-
// rotate 处理成 rotateZ
|
|
96
|
-
key = key === 'rotate' ? 'rotateZ' : key;
|
|
97
|
-
// 单个值处理
|
|
98
|
-
transform.push({ [key]: global.__formatValue(val) });
|
|
99
|
-
break;
|
|
100
|
-
case 'matrix':
|
|
101
|
-
transform.push({ [key]: parseValues(val, ',').map(val => +val) });
|
|
102
|
-
break;
|
|
103
|
-
case 'translate':
|
|
104
|
-
case 'scale':
|
|
105
|
-
case 'skew':
|
|
106
|
-
case 'translate3d': // x y 支持 z不支持
|
|
107
|
-
case 'scale3d': // x y 支持 z不支持
|
|
108
|
-
{
|
|
109
|
-
// 2 个以上的值处理
|
|
110
|
-
key = key.replace('3d', '');
|
|
111
|
-
const vals = parseValues(val, ',').splice(0, 3);
|
|
112
|
-
// scale(.5) === scaleX(.5) scaleY(.5)
|
|
113
|
-
if (vals.length === 1 && key === 'scale') {
|
|
114
|
-
vals.push(vals[0]);
|
|
115
|
-
}
|
|
116
|
-
const xyz = ['X', 'Y', 'Z'];
|
|
117
|
-
transform.push(...vals.map((v, index) => {
|
|
118
|
-
return { [`${key}${xyz[index] || ''}`]: global.__formatValue(v.trim()) };
|
|
119
|
-
}));
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
return transform;
|
|
126
|
-
};
|
|
127
|
-
// format style
|
|
128
|
-
const formatStyle = (style) => {
|
|
129
|
-
if (!style.transform || Array.isArray(style.transform))
|
|
130
|
-
return style;
|
|
131
|
-
return Object.assign({}, style, {
|
|
132
|
-
transform: parseTransform(style.transform)
|
|
133
|
-
});
|
|
134
|
-
};
|
|
135
51
|
// transform 数组转对象
|
|
136
52
|
function getTransformObj(transforms) {
|
|
137
53
|
'worklet';
|
|
@@ -140,7 +56,7 @@ function getTransformObj(transforms) {
|
|
|
140
56
|
}, {});
|
|
141
57
|
}
|
|
142
58
|
export default function useAnimationHooks(props) {
|
|
143
|
-
const { style = {}, animation, enableAnimation, transitionend, layoutRef } = props;
|
|
59
|
+
const { style: originalStyle = {}, animation, enableAnimation, transitionend, layoutRef } = props;
|
|
144
60
|
const enableStyleAnimation = enableAnimation || !!animation;
|
|
145
61
|
const enableAnimationRef = useRef(enableStyleAnimation);
|
|
146
62
|
if (enableAnimationRef.current !== enableStyleAnimation) {
|
|
@@ -148,7 +64,6 @@ export default function useAnimationHooks(props) {
|
|
|
148
64
|
}
|
|
149
65
|
if (!enableAnimationRef.current)
|
|
150
66
|
return { enableStyleAnimation: false };
|
|
151
|
-
const originalStyle = formatStyle(style);
|
|
152
67
|
// id 标识
|
|
153
68
|
const id = animation?.id || -1;
|
|
154
69
|
// 有动画样式的 style key
|
|
@@ -177,7 +92,7 @@ export default function useAnimationHooks(props) {
|
|
|
177
92
|
useEffect(() => {
|
|
178
93
|
// style 更新后同步更新 lastStyleRef & shareValMap
|
|
179
94
|
updateStyleVal();
|
|
180
|
-
}, [
|
|
95
|
+
}, [originalStyle]);
|
|
181
96
|
// ** 获取动画样式prop & 驱动动画
|
|
182
97
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
183
98
|
useEffect(() => {
|
|
@@ -24,7 +24,7 @@ const getTouchEvent = (
|
|
|
24
24
|
) => {
|
|
25
25
|
const { navigation, propsRef, layoutRef } = config
|
|
26
26
|
const props = propsRef.current
|
|
27
|
-
const {
|
|
27
|
+
const { top: navigationY = 0 } = navigation?.layout || {}
|
|
28
28
|
const nativeEvent = event.nativeEvent
|
|
29
29
|
const { timestamp, pageX, pageY, touches, changedTouches } = nativeEvent
|
|
30
30
|
const { id } = props
|