@mpxjs/webpack-plugin 2.10.4-beta.18 → 2.10.4-beta.19-input
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/runtime/components/react/dist/getInnerListeners.js +36 -22
- package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
- package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +20 -17
- package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-checkbox.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-icon/index.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-image.jsx +33 -20
- package/lib/runtime/components/react/dist/mpx-input.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-label.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +8 -3
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +205 -79
- package/lib/runtime/components/react/dist/mpx-picker/index.jsx +11 -13
- package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +8 -7
- package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +29 -11
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
- package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
- package/lib/runtime/components/react/dist/mpx-radio-group.jsx +9 -2
- package/lib/runtime/components/react/dist/mpx-radio.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -2
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +104 -51
- package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
- package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +3 -1
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +11 -9
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +203 -141
- package/lib/runtime/components/react/dist/mpx-switch.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-text.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-video.jsx +7 -2
- package/lib/runtime/components/react/dist/mpx-view.jsx +28 -26
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +34 -29
- package/lib/runtime/components/react/dist/useAnimationHooks.js +12 -89
- package/lib/runtime/components/react/dist/utils.jsx +199 -114
- package/lib/runtime/components/react/mpx-input.tsx +6 -6
- package/lib/template-compiler/compiler.js +1 -1
- package/package.json +1 -1
|
@@ -7,9 +7,10 @@ import useNodesRef from './useNodesRef';
|
|
|
7
7
|
import useInnerProps from './getInnerListeners';
|
|
8
8
|
import { MovableAreaContext } from './context';
|
|
9
9
|
import { useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
|
|
10
|
+
import Portal from './mpx-portal';
|
|
10
11
|
const _MovableArea = forwardRef((props, ref) => {
|
|
11
12
|
const { style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
|
|
12
|
-
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
13
|
+
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
13
14
|
const movableViewRef = useRef(null);
|
|
14
15
|
useNodesRef(props, ref, movableViewRef, {
|
|
15
16
|
style: normalStyle
|
|
@@ -20,13 +21,17 @@ const _MovableArea = forwardRef((props, ref) => {
|
|
|
20
21
|
}), [normalStyle.width, normalStyle.height]);
|
|
21
22
|
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: movableViewRef });
|
|
22
23
|
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
23
|
-
style: extendObject({ height: contextValue.height, width: contextValue.width
|
|
24
|
+
style: extendObject({ height: contextValue.height, width: contextValue.width }, normalStyle, layoutStyle),
|
|
24
25
|
ref: movableViewRef
|
|
25
26
|
}), [], { layoutRef });
|
|
26
|
-
|
|
27
|
+
let movableComponent = createElement(MovableAreaContext.Provider, { value: contextValue }, createElement(View, innerProps, wrapChildren(props, {
|
|
27
28
|
hasVarDec,
|
|
28
29
|
varContext: varContextRef.current
|
|
29
30
|
})));
|
|
31
|
+
if (hasPositionFixed) {
|
|
32
|
+
movableComponent = createElement(Portal, null, movableComponent);
|
|
33
|
+
}
|
|
34
|
+
return movableComponent;
|
|
30
35
|
});
|
|
31
36
|
_MovableArea.displayName = 'MpxMovableArea';
|
|
32
37
|
export default _MovableArea;
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* ✔ out-of-bounds
|
|
5
5
|
* ✔ x
|
|
6
6
|
* ✔ y
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* ✔ damping
|
|
8
|
+
* ✔ friction
|
|
9
9
|
* ✔ disabled
|
|
10
10
|
* ✘ scale
|
|
11
11
|
* ✘ scale-min
|
|
@@ -22,10 +22,98 @@ import { StyleSheet } from 'react-native';
|
|
|
22
22
|
import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
23
23
|
import useNodesRef from './useNodesRef';
|
|
24
24
|
import { MovableAreaContext } from './context';
|
|
25
|
-
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, flatGesture, extendObject, omit, useNavigation } from './utils';
|
|
25
|
+
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, flatGesture, extendObject, omit, useNavigation, useRunOnJSCallback } from './utils';
|
|
26
26
|
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
|
|
27
|
-
import Animated, { useSharedValue, useAnimatedStyle,
|
|
27
|
+
import Animated, { useSharedValue, useAnimatedStyle, runOnJS, runOnUI, withTiming, Easing } from 'react-native-reanimated';
|
|
28
28
|
import { collectDataset, noop } from '@mpxjs/utils';
|
|
29
|
+
// 超出边界处理函数,参考微信小程序的超出边界衰减效果
|
|
30
|
+
const applyBoundaryDecline = (newValue, range) => {
|
|
31
|
+
'worklet';
|
|
32
|
+
const decline = (distance) => {
|
|
33
|
+
'worklet';
|
|
34
|
+
return Math.sqrt(Math.abs(distance));
|
|
35
|
+
};
|
|
36
|
+
if (newValue < range[0]) {
|
|
37
|
+
const overDistance = range[0] - newValue;
|
|
38
|
+
return range[0] - decline(overDistance);
|
|
39
|
+
}
|
|
40
|
+
else if (newValue > range[1]) {
|
|
41
|
+
const overDistance = newValue - range[1];
|
|
42
|
+
return range[1] + decline(overDistance);
|
|
43
|
+
}
|
|
44
|
+
return newValue;
|
|
45
|
+
};
|
|
46
|
+
// 参考微信小程序的弹簧阻尼系统实现
|
|
47
|
+
const withWechatSpring = (toValue, dampingParam = 20, callback) => {
|
|
48
|
+
'worklet';
|
|
49
|
+
// 弹簧参数计算
|
|
50
|
+
const m = 1; // 质量
|
|
51
|
+
const k = 9 * Math.pow(dampingParam, 2) / 40; // 弹簧系数
|
|
52
|
+
const c = dampingParam; // 阻尼系数
|
|
53
|
+
// 判别式:r = c² - 4mk
|
|
54
|
+
const discriminant = c * c - 4 * m * k;
|
|
55
|
+
// 计算动画持续时间和缓动函数
|
|
56
|
+
let duration;
|
|
57
|
+
let easingFunction;
|
|
58
|
+
if (Math.abs(discriminant) < 0.01) {
|
|
59
|
+
// 临界阻尼 (discriminant ≈ 0)
|
|
60
|
+
// 使用cubic-out模拟临界阻尼的平滑过渡
|
|
61
|
+
duration = Math.max(350, Math.min(800, 2000 / dampingParam));
|
|
62
|
+
easingFunction = Easing.out(Easing.cubic);
|
|
63
|
+
}
|
|
64
|
+
else if (discriminant > 0) {
|
|
65
|
+
// 过阻尼 (discriminant > 0)
|
|
66
|
+
// 使用指数缓动模拟过阻尼的缓慢收敛
|
|
67
|
+
duration = Math.max(450, Math.min(1000, 2500 / dampingParam));
|
|
68
|
+
easingFunction = Easing.out(Easing.exp);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// 欠阻尼 (discriminant < 0) - 会产生振荡
|
|
72
|
+
// 计算振荡频率和衰减率
|
|
73
|
+
const dampingRatio = c / (2 * Math.sqrt(m * k)); // 阻尼比
|
|
74
|
+
// 根据阻尼比调整动画参数
|
|
75
|
+
if (dampingRatio < 0.7) {
|
|
76
|
+
// 明显振荡
|
|
77
|
+
duration = Math.max(600, Math.min(1200, 3000 / dampingParam));
|
|
78
|
+
// 创建带振荡的贝塞尔曲线
|
|
79
|
+
easingFunction = Easing.bezier(0.175, 0.885, 0.32, 1.275);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// 轻微振荡
|
|
83
|
+
duration = Math.max(400, Math.min(800, 2000 / dampingParam));
|
|
84
|
+
easingFunction = Easing.bezier(0.25, 0.46, 0.45, 0.94);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return withTiming(toValue, {
|
|
88
|
+
duration,
|
|
89
|
+
easing: easingFunction
|
|
90
|
+
}, callback);
|
|
91
|
+
};
|
|
92
|
+
// 参考微信小程序friction的惯性动画
|
|
93
|
+
const withWechatDecay = (velocity, currentPosition, clampRange, frictionValue = 2, callback) => {
|
|
94
|
+
'worklet';
|
|
95
|
+
// 微信小程序friction算法: delta = -1.5 * v² / a, 其中 a = -f * v / |v|
|
|
96
|
+
// 如果friction小于等于0,设置为默认值2
|
|
97
|
+
const validFriction = frictionValue <= 0 ? 2 : frictionValue;
|
|
98
|
+
const f = 1000 * validFriction;
|
|
99
|
+
const acceleration = velocity !== 0 ? -f * velocity / Math.abs(velocity) : 0;
|
|
100
|
+
const delta = acceleration !== 0 ? (-1.5 * velocity * velocity) / acceleration : 0;
|
|
101
|
+
let finalPosition = currentPosition + delta;
|
|
102
|
+
// 边界限制
|
|
103
|
+
if (finalPosition < clampRange[0]) {
|
|
104
|
+
finalPosition = clampRange[0];
|
|
105
|
+
}
|
|
106
|
+
else if (finalPosition > clampRange[1]) {
|
|
107
|
+
finalPosition = clampRange[1];
|
|
108
|
+
}
|
|
109
|
+
// 计算动画时长
|
|
110
|
+
const distance = Math.abs(finalPosition - currentPosition);
|
|
111
|
+
const duration = Math.min(1500, Math.max(200, distance * 8));
|
|
112
|
+
return withTiming(finalPosition, {
|
|
113
|
+
duration,
|
|
114
|
+
easing: Easing.out(Easing.cubic)
|
|
115
|
+
}, callback);
|
|
116
|
+
};
|
|
29
117
|
const styles = StyleSheet.create({
|
|
30
118
|
container: {
|
|
31
119
|
position: 'absolute',
|
|
@@ -41,7 +129,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
41
129
|
const hasLayoutRef = useRef(false);
|
|
42
130
|
const propsRef = useRef({});
|
|
43
131
|
propsRef.current = (props || {});
|
|
44
|
-
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend } = props;
|
|
132
|
+
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, damping = 20, friction = 2, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'disable-event-passthrough': disableEventPassthrough = false, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, changeThrottleTime = 60, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend, bindchange } = props;
|
|
45
133
|
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(Object.assign({}, style, styles.container), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
46
134
|
const navigation = useNavigation();
|
|
47
135
|
const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []);
|
|
@@ -61,6 +149,8 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
61
149
|
const yInertialMotion = useSharedValue(false);
|
|
62
150
|
const isFirstTouch = useSharedValue(true);
|
|
63
151
|
const touchEvent = useSharedValue('');
|
|
152
|
+
const initialViewPosition = useSharedValue({ x: x || 0, y: y || 0 });
|
|
153
|
+
const lastChangeTime = useSharedValue(0);
|
|
64
154
|
const MovableAreaLayout = useContext(MovableAreaContext);
|
|
65
155
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
66
156
|
const waitForHandlers = flatGesture(waitFor);
|
|
@@ -104,25 +194,21 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
104
194
|
const { x: newX, y: newY } = checkBoundaryPosition({ positionX: Number(x), positionY: Number(y) });
|
|
105
195
|
if (direction === 'horizontal' || direction === 'all') {
|
|
106
196
|
offsetX.value = animation
|
|
107
|
-
?
|
|
108
|
-
duration: 1500,
|
|
109
|
-
dampingRatio: 0.8
|
|
110
|
-
})
|
|
197
|
+
? withWechatSpring(newX, damping)
|
|
111
198
|
: newX;
|
|
112
199
|
}
|
|
113
200
|
if (direction === 'vertical' || direction === 'all') {
|
|
114
201
|
offsetY.value = animation
|
|
115
|
-
?
|
|
116
|
-
duration: 1500,
|
|
117
|
-
dampingRatio: 0.8
|
|
118
|
-
})
|
|
202
|
+
? withWechatSpring(newY, damping)
|
|
119
203
|
: newY;
|
|
120
204
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
205
|
+
if (bindchange) {
|
|
206
|
+
runOnJS(runOnJSCallback)('handleTriggerChange', {
|
|
207
|
+
x: newX,
|
|
208
|
+
y: newY,
|
|
209
|
+
type: 'setData'
|
|
210
|
+
});
|
|
211
|
+
}
|
|
126
212
|
}
|
|
127
213
|
})();
|
|
128
214
|
}, [x, y]);
|
|
@@ -132,16 +218,6 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
132
218
|
resetBoundaryAndCheck({ width, height });
|
|
133
219
|
}
|
|
134
220
|
}, [MovableAreaLayout.height, MovableAreaLayout.width]);
|
|
135
|
-
useAnimatedReaction(() => ({
|
|
136
|
-
offsetX: offsetX.value,
|
|
137
|
-
offsetY: offsetY.value
|
|
138
|
-
}), (currentValue) => {
|
|
139
|
-
const { offsetX, offsetY } = currentValue;
|
|
140
|
-
runOnJS(handleTriggerChange)({
|
|
141
|
-
x: offsetX,
|
|
142
|
-
y: offsetY
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
221
|
const getTouchSource = useCallback((offsetX, offsetY) => {
|
|
146
222
|
const hasOverBoundary = offsetX < draggableXRange.value[0] || offsetX > draggableXRange.value[1] ||
|
|
147
223
|
offsetY < draggableYRange.value[0] || offsetY > draggableYRange.value[1];
|
|
@@ -230,14 +306,14 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
230
306
|
setHeight(height || 0);
|
|
231
307
|
}
|
|
232
308
|
nodeRef.current?.measure((x, y, width, height) => {
|
|
233
|
-
const {
|
|
309
|
+
const { top: navigationY = 0 } = navigation?.layout || {};
|
|
234
310
|
layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 };
|
|
235
311
|
resetBoundaryAndCheck({ width, height });
|
|
236
312
|
});
|
|
237
313
|
props.onLayout && props.onLayout(e);
|
|
238
314
|
};
|
|
239
315
|
const extendEvent = useCallback((e, type) => {
|
|
240
|
-
const {
|
|
316
|
+
const { top: navigationY = 0 } = navigation?.layout || {};
|
|
241
317
|
const touchArr = [e.changedTouches, e.allTouches];
|
|
242
318
|
touchArr.forEach(touches => {
|
|
243
319
|
touches && touches.forEach((item) => {
|
|
@@ -259,11 +335,13 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
259
335
|
});
|
|
260
336
|
}, []);
|
|
261
337
|
const triggerStartOnJS = ({ e }) => {
|
|
338
|
+
const { bindtouchstart, catchtouchstart } = propsRef.current;
|
|
262
339
|
extendEvent(e, 'start');
|
|
263
340
|
bindtouchstart && bindtouchstart(e);
|
|
264
341
|
catchtouchstart && catchtouchstart(e);
|
|
265
342
|
};
|
|
266
343
|
const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }) => {
|
|
344
|
+
const { bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove } = propsRef.current;
|
|
267
345
|
extendEvent(e, 'move');
|
|
268
346
|
if (hasTouchmove) {
|
|
269
347
|
if (touchEvent === 'htouchmove') {
|
|
@@ -285,17 +363,34 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
285
363
|
}
|
|
286
364
|
};
|
|
287
365
|
const triggerEndOnJS = ({ e }) => {
|
|
366
|
+
const { bindtouchend, catchtouchend } = propsRef.current;
|
|
288
367
|
extendEvent(e, 'end');
|
|
289
368
|
bindtouchend && bindtouchend(e);
|
|
290
369
|
catchtouchend && catchtouchend(e);
|
|
291
370
|
};
|
|
371
|
+
const runOnJSCallbackRef = useRef({
|
|
372
|
+
handleTriggerChange,
|
|
373
|
+
triggerStartOnJS,
|
|
374
|
+
triggerMoveOnJS,
|
|
375
|
+
triggerEndOnJS
|
|
376
|
+
});
|
|
377
|
+
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
|
|
378
|
+
// 节流版本的change事件触发
|
|
379
|
+
const handleTriggerChangeThrottled = useCallback(({ x, y, type }) => {
|
|
380
|
+
'worklet';
|
|
381
|
+
const now = Date.now();
|
|
382
|
+
if (now - lastChangeTime.value >= changeThrottleTime) {
|
|
383
|
+
lastChangeTime.value = now;
|
|
384
|
+
runOnJS(runOnJSCallback)('handleTriggerChange', { x, y, type });
|
|
385
|
+
}
|
|
386
|
+
}, [changeThrottleTime]);
|
|
292
387
|
const gesture = useMemo(() => {
|
|
293
388
|
const handleTriggerMove = (e) => {
|
|
294
389
|
'worklet';
|
|
295
390
|
const hasTouchmove = !!bindhtouchmove || !!bindvtouchmove || !!bindtouchmove;
|
|
296
391
|
const hasCatchTouchmove = !!catchhtouchmove || !!catchvtouchmove || !!catchtouchmove;
|
|
297
392
|
if (hasTouchmove || hasCatchTouchmove) {
|
|
298
|
-
runOnJS(
|
|
393
|
+
runOnJS(runOnJSCallback)('triggerMoveOnJS', {
|
|
299
394
|
e,
|
|
300
395
|
touchEvent: touchEvent.value,
|
|
301
396
|
hasTouchmove,
|
|
@@ -313,8 +408,15 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
313
408
|
y: changedTouches.y
|
|
314
409
|
};
|
|
315
410
|
if (bindtouchstart || catchtouchstart) {
|
|
316
|
-
runOnJS(
|
|
411
|
+
runOnJS(runOnJSCallback)('triggerStartOnJS', { e });
|
|
317
412
|
}
|
|
413
|
+
})
|
|
414
|
+
.onStart(() => {
|
|
415
|
+
'worklet';
|
|
416
|
+
initialViewPosition.value = {
|
|
417
|
+
x: offsetX.value,
|
|
418
|
+
y: offsetY.value
|
|
419
|
+
};
|
|
318
420
|
})
|
|
319
421
|
.onTouchesMove((e) => {
|
|
320
422
|
'worklet';
|
|
@@ -325,87 +427,111 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
325
427
|
isFirstTouch.value = false;
|
|
326
428
|
}
|
|
327
429
|
handleTriggerMove(e);
|
|
430
|
+
})
|
|
431
|
+
.onUpdate((e) => {
|
|
432
|
+
'worklet';
|
|
328
433
|
if (disabled)
|
|
329
434
|
return;
|
|
330
|
-
const changeX = changedTouches.x - startPosition.value.x;
|
|
331
|
-
const changeY = changedTouches.y - startPosition.value.y;
|
|
332
435
|
if (direction === 'horizontal' || direction === 'all') {
|
|
333
|
-
const newX =
|
|
436
|
+
const newX = initialViewPosition.value.x + e.translationX;
|
|
334
437
|
if (!outOfBounds) {
|
|
335
438
|
const { x } = checkBoundaryPosition({ positionX: newX, positionY: offsetY.value });
|
|
336
439
|
offsetX.value = x;
|
|
337
440
|
}
|
|
338
441
|
else {
|
|
339
|
-
offsetX.value = newX;
|
|
442
|
+
offsetX.value = applyBoundaryDecline(newX, draggableXRange.value);
|
|
340
443
|
}
|
|
341
444
|
}
|
|
342
445
|
if (direction === 'vertical' || direction === 'all') {
|
|
343
|
-
const newY =
|
|
446
|
+
const newY = initialViewPosition.value.y + e.translationY;
|
|
344
447
|
if (!outOfBounds) {
|
|
345
448
|
const { y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: newY });
|
|
346
449
|
offsetY.value = y;
|
|
347
450
|
}
|
|
348
451
|
else {
|
|
349
|
-
offsetY.value = newY;
|
|
452
|
+
offsetY.value = applyBoundaryDecline(newY, draggableYRange.value);
|
|
350
453
|
}
|
|
351
454
|
}
|
|
455
|
+
if (bindchange) {
|
|
456
|
+
// 使用节流版本减少 runOnJS 调用
|
|
457
|
+
handleTriggerChangeThrottled({
|
|
458
|
+
x: offsetX.value,
|
|
459
|
+
y: offsetY.value
|
|
460
|
+
});
|
|
461
|
+
}
|
|
352
462
|
})
|
|
353
463
|
.onTouchesUp((e) => {
|
|
354
464
|
'worklet';
|
|
355
465
|
isFirstTouch.value = true;
|
|
356
466
|
isMoving.value = false;
|
|
357
467
|
if (bindtouchend || catchtouchend) {
|
|
358
|
-
runOnJS(
|
|
468
|
+
runOnJS(runOnJSCallback)('triggerEndOnJS', { e });
|
|
359
469
|
}
|
|
470
|
+
})
|
|
471
|
+
.onEnd((e) => {
|
|
472
|
+
'worklet';
|
|
473
|
+
isMoving.value = false;
|
|
360
474
|
if (disabled)
|
|
361
475
|
return;
|
|
362
|
-
|
|
476
|
+
// 处理没有惯性且超出边界的回弹
|
|
477
|
+
if (!inertia && outOfBounds) {
|
|
363
478
|
const { x, y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: offsetY.value });
|
|
364
|
-
if (x !== offsetX.value) {
|
|
365
|
-
offsetX.value
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
479
|
+
if (x !== offsetX.value || y !== offsetY.value) {
|
|
480
|
+
if (x !== offsetX.value) {
|
|
481
|
+
offsetX.value = animation
|
|
482
|
+
? withWechatSpring(x, damping)
|
|
483
|
+
: x;
|
|
484
|
+
}
|
|
485
|
+
if (y !== offsetY.value) {
|
|
486
|
+
offsetY.value = animation
|
|
487
|
+
? withWechatSpring(y, damping)
|
|
488
|
+
: y;
|
|
489
|
+
}
|
|
490
|
+
if (bindchange) {
|
|
491
|
+
runOnJS(runOnJSCallback)('handleTriggerChange', {
|
|
492
|
+
x,
|
|
493
|
+
y
|
|
494
|
+
});
|
|
495
|
+
}
|
|
371
496
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
497
|
+
}
|
|
498
|
+
else if (inertia) {
|
|
499
|
+
// 惯性处理 - 使用微信小程序friction算法
|
|
500
|
+
if (direction === 'horizontal' || direction === 'all') {
|
|
501
|
+
xInertialMotion.value = true;
|
|
502
|
+
offsetX.value = withWechatDecay(e.velocityX / 10, offsetX.value, draggableXRange.value, friction, () => {
|
|
503
|
+
xInertialMotion.value = false;
|
|
504
|
+
if (bindchange) {
|
|
505
|
+
runOnJS(runOnJSCallback)('handleTriggerChange', {
|
|
506
|
+
x: offsetX.value,
|
|
507
|
+
y: offsetY.value
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (direction === 'vertical' || direction === 'all') {
|
|
513
|
+
yInertialMotion.value = true;
|
|
514
|
+
offsetY.value = withWechatDecay(e.velocityY / 10, offsetY.value, draggableYRange.value, friction, () => {
|
|
515
|
+
yInertialMotion.value = false;
|
|
516
|
+
if (bindchange) {
|
|
517
|
+
runOnJS(runOnJSCallback)('handleTriggerChange', {
|
|
518
|
+
x: offsetX.value,
|
|
519
|
+
y: offsetY.value
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
});
|
|
379
523
|
}
|
|
380
524
|
}
|
|
381
525
|
})
|
|
382
|
-
.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return;
|
|
387
|
-
if (direction === 'horizontal' || direction === 'all') {
|
|
388
|
-
xInertialMotion.value = true;
|
|
389
|
-
offsetX.value = withDecay({
|
|
390
|
-
velocity: e.velocityX / 10,
|
|
391
|
-
rubberBandEffect: outOfBounds,
|
|
392
|
-
clamp: draggableXRange.value
|
|
393
|
-
}, () => {
|
|
394
|
-
xInertialMotion.value = false;
|
|
395
|
-
});
|
|
526
|
+
.withRef(movableGestureRef);
|
|
527
|
+
if (!disableEventPassthrough) {
|
|
528
|
+
if (direction === 'horizontal') {
|
|
529
|
+
gesturePan.activeOffsetX([-5, 5]).failOffsetY([-5, 5]);
|
|
396
530
|
}
|
|
397
|
-
if (direction === 'vertical'
|
|
398
|
-
|
|
399
|
-
offsetY.value = withDecay({
|
|
400
|
-
velocity: e.velocityY / 10,
|
|
401
|
-
rubberBandEffect: outOfBounds,
|
|
402
|
-
clamp: draggableYRange.value
|
|
403
|
-
}, () => {
|
|
404
|
-
yInertialMotion.value = false;
|
|
405
|
-
});
|
|
531
|
+
else if (direction === 'vertical') {
|
|
532
|
+
gesturePan.activeOffsetY([-5, 5]).failOffsetX([-5, 5]);
|
|
406
533
|
}
|
|
407
|
-
}
|
|
408
|
-
.withRef(movableGestureRef);
|
|
534
|
+
}
|
|
409
535
|
if (simultaneousHandlers && simultaneousHandlers.length) {
|
|
410
536
|
gesturePan.simultaneousWithExternalGesture(...simultaneousHandlers);
|
|
411
537
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef, useRef, useContext, useEffect } from 'react';
|
|
1
|
+
import React, { forwardRef, useRef, useContext, useEffect, createElement } from 'react';
|
|
2
2
|
import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
|
|
3
3
|
import { warn } from '@mpxjs/utils';
|
|
4
4
|
import PickerSelector from './selector';
|
|
@@ -9,7 +9,7 @@ import PickerRegion from './region';
|
|
|
9
9
|
import { FormContext, RouteContext } from '../context';
|
|
10
10
|
import useNodesRef from '../useNodesRef';
|
|
11
11
|
import useInnerProps, { getCustomEvent } from '../getInnerListeners';
|
|
12
|
-
import { extendObject } from '../utils';
|
|
12
|
+
import { extendObject, useLayout } from '../utils';
|
|
13
13
|
import { createPopupManager } from '../mpx-popup';
|
|
14
14
|
/**
|
|
15
15
|
* ✔ mode
|
|
@@ -109,17 +109,18 @@ const Picker = forwardRef((props, ref) => {
|
|
|
109
109
|
const buttonText = buttonTextMap[global.__mpx?.i18n?.locale || 'zh-CN'];
|
|
110
110
|
const pickerValue = useRef(value);
|
|
111
111
|
pickerValue.current = Array.isArray(value) ? value.slice() : value;
|
|
112
|
-
const innerLayout = useRef({});
|
|
113
112
|
const nodeRef = useRef(null);
|
|
114
113
|
const pickerRef = useRef(null);
|
|
115
114
|
const { open, show, hide, remove } = useRef(createPopupManager()).current;
|
|
116
115
|
useNodesRef(props, ref, nodeRef);
|
|
116
|
+
const { layoutRef, layoutProps } = useLayout({
|
|
117
|
+
props,
|
|
118
|
+
hasSelfPercent: false,
|
|
119
|
+
nodeRef
|
|
120
|
+
});
|
|
117
121
|
const innerProps = useInnerProps(extendObject({}, props, {
|
|
118
122
|
ref: nodeRef
|
|
119
|
-
}), [], { layoutRef
|
|
120
|
-
const getInnerLayout = (layout) => {
|
|
121
|
-
innerLayout.current = layout.current;
|
|
122
|
-
};
|
|
123
|
+
}, layoutProps), [], { layoutRef });
|
|
123
124
|
useEffect(() => {
|
|
124
125
|
if (range && pickerRef.current && mode === "multiSelector" /* PickerMode.MULTI_SELECTOR */) {
|
|
125
126
|
pickerRef.current.updateRange?.(range);
|
|
@@ -162,7 +163,7 @@ const Picker = forwardRef((props, ref) => {
|
|
|
162
163
|
if (mode !== "multiSelector" /* PickerMode.MULTI_SELECTOR */) {
|
|
163
164
|
return;
|
|
164
165
|
}
|
|
165
|
-
const eventData = getCustomEvent('columnchange', {}, { detail: { column: columnIndex, value }, layoutRef
|
|
166
|
+
const eventData = getCustomEvent('columnchange', {}, { detail: { column: columnIndex, value }, layoutRef });
|
|
166
167
|
props.bindcolumnchange?.(eventData);
|
|
167
168
|
};
|
|
168
169
|
const onCancel = () => {
|
|
@@ -170,7 +171,7 @@ const Picker = forwardRef((props, ref) => {
|
|
|
170
171
|
hide();
|
|
171
172
|
};
|
|
172
173
|
const onConfirm = () => {
|
|
173
|
-
const eventData = getCustomEvent('change', {}, { detail: { value: pickerValue.current }, layoutRef
|
|
174
|
+
const eventData = getCustomEvent('change', {}, { detail: { value: pickerValue.current }, layoutRef });
|
|
174
175
|
bindchange?.(eventData);
|
|
175
176
|
hide();
|
|
176
177
|
};
|
|
@@ -179,7 +180,6 @@ const Picker = forwardRef((props, ref) => {
|
|
|
179
180
|
children,
|
|
180
181
|
bindchange: onChange,
|
|
181
182
|
bindcolumnchange: onColumnChange,
|
|
182
|
-
getInnerLayout,
|
|
183
183
|
getRange: () => range
|
|
184
184
|
});
|
|
185
185
|
const renderPickerContent = () => {
|
|
@@ -215,9 +215,7 @@ const Picker = forwardRef((props, ref) => {
|
|
|
215
215
|
remove();
|
|
216
216
|
};
|
|
217
217
|
}, []);
|
|
218
|
-
return (
|
|
219
|
-
{children}
|
|
220
|
-
</TouchableWithoutFeedback>);
|
|
218
|
+
return createElement(TouchableWithoutFeedback, { onPress: show }, createElement(View, innerProps, children));
|
|
221
219
|
});
|
|
222
220
|
Picker.displayName = 'MpxPicker';
|
|
223
221
|
export default Picker;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { View } from 'react-native';
|
|
2
|
-
import React, { forwardRef, useRef } from 'react';
|
|
2
|
+
import React, { createElement, forwardRef, useRef } from 'react';
|
|
3
3
|
import useInnerProps, { getCustomEvent } from '../getInnerListeners';
|
|
4
4
|
import useNodesRef from '../useNodesRef';
|
|
5
5
|
import { useLayout, splitProps, splitStyle, wrapChildren, useTransformStyle, extendObject } from '../utils';
|
|
6
6
|
import { PickerViewStyleContext } from './pickerVIewContext';
|
|
7
|
+
import Portal from '../mpx-portal';
|
|
7
8
|
const styles = {
|
|
8
9
|
wrapper: {
|
|
9
10
|
display: 'flex',
|
|
@@ -23,7 +24,7 @@ const _PickerView = forwardRef((props, ref) => {
|
|
|
23
24
|
const activeValueRef = useRef(value);
|
|
24
25
|
activeValueRef.current = value.slice();
|
|
25
26
|
const snapActiveValueRef = useRef(null);
|
|
26
|
-
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
|
|
27
|
+
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(style, { enableVar, externalVarContext });
|
|
27
28
|
useNodesRef(props, ref, nodeRef, {
|
|
28
29
|
style: normalStyle
|
|
29
30
|
});
|
|
@@ -114,11 +115,11 @@ const _PickerView = forwardRef((props, ref) => {
|
|
|
114
115
|
onInitialChange(isInvalid, validValue);
|
|
115
116
|
return renderColumns;
|
|
116
117
|
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
const finalComponent = createElement(PickerViewStyleContext.Provider, { value: textStyle }, createElement(View, innerProps, createElement(View, { style: [styles.wrapper] }, renderPickerColumns())));
|
|
119
|
+
if (hasPositionFixed) {
|
|
120
|
+
return createElement(Portal, null, finalComponent);
|
|
121
|
+
}
|
|
122
|
+
return finalComponent;
|
|
122
123
|
});
|
|
123
124
|
_PickerView.displayName = 'MpxPickerView';
|
|
124
125
|
export default _PickerView;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
|
|
1
|
+
import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback, createElement } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
|
|
4
|
-
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony } from '../utils';
|
|
4
|
+
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony, extendObject } from '../utils';
|
|
5
5
|
import useNodesRef from '../useNodesRef';
|
|
6
6
|
import PickerIndicator from './pickerViewIndicator';
|
|
7
7
|
import PickerMask from './pickerViewMask';
|
|
@@ -157,8 +157,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
157
157
|
}, [itemRawH, maxIndex, snapToOffsets, onMomentumScrollEnd, resetScrollPosition]);
|
|
158
158
|
const onScroll = useCallback((e) => {
|
|
159
159
|
// 全局注册的振动触感 hook
|
|
160
|
-
const
|
|
161
|
-
if (typeof
|
|
160
|
+
const onPickerVibrate = global.__mpx?.config?.rnConfig?.onPickerVibrate;
|
|
161
|
+
if (typeof onPickerVibrate !== 'function') {
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
164
|
const { y } = e.nativeEvent.contentOffset;
|
|
@@ -172,7 +172,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
172
172
|
y: currentId * itemRawH
|
|
173
173
|
};
|
|
174
174
|
// vibrateShort({ type: 'selection' })
|
|
175
|
-
|
|
175
|
+
onPickerVibrate();
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
}
|
|
@@ -220,11 +220,28 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
220
220
|
return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
|
|
221
221
|
});
|
|
222
222
|
const renderScollView = () => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
const innerProps = extendObject({}, layoutProps, {
|
|
224
|
+
ref: scrollViewRef,
|
|
225
|
+
bounces: true,
|
|
226
|
+
horizontal: false,
|
|
227
|
+
nestedScrollEnabled: true,
|
|
228
|
+
removeClippedSubviews: false,
|
|
229
|
+
showsVerticalScrollIndicator: false,
|
|
230
|
+
showsHorizontalScrollIndicator: false,
|
|
231
|
+
scrollEventThrottle: 16,
|
|
232
|
+
style: styles.scrollView,
|
|
233
|
+
decelerationRate: 'fast',
|
|
234
|
+
snapToOffsets: snapToOffsets,
|
|
235
|
+
onTouchEnd: onClickOnceItem,
|
|
236
|
+
onScroll,
|
|
237
|
+
onScrollBeginDrag,
|
|
238
|
+
onScrollEndDrag,
|
|
239
|
+
onMomentumScrollBegin,
|
|
240
|
+
onMomentumScrollEnd,
|
|
241
|
+
onContentSizeChange,
|
|
242
|
+
contentContainerStyle
|
|
243
|
+
});
|
|
244
|
+
return createElement(PickerViewColumnAnimationContext.Provider, { value: offsetYShared }, createElement(Reanimated.ScrollView, innerProps, renderInnerchild()));
|
|
228
245
|
};
|
|
229
246
|
const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
|
|
230
247
|
const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
|
|
@@ -235,7 +252,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
235
252
|
</View>);
|
|
236
253
|
});
|
|
237
254
|
const styles = StyleSheet.create({
|
|
238
|
-
wrapper: { display: 'flex', flex: 1 }
|
|
255
|
+
wrapper: { display: 'flex', flex: 1 },
|
|
256
|
+
scrollView: { width: '100%' }
|
|
239
257
|
});
|
|
240
258
|
_PickerViewColumn.displayName = 'MpxPickerViewColumn';
|
|
241
259
|
export default _PickerViewColumn;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
|
|
2
|
-
import { View, StyleSheet } from 'react-native';
|
|
1
|
+
import { useState, useCallback, forwardRef, useImperativeHandle, Fragment } from 'react';
|
|
3
2
|
const _PortalManager = forwardRef((props, ref) => {
|
|
4
3
|
const [state, setState] = useState({
|
|
5
4
|
portals: []
|
|
@@ -31,10 +30,9 @@ const _PortalManager = forwardRef((props, ref) => {
|
|
|
31
30
|
portals: state.portals
|
|
32
31
|
}));
|
|
33
32
|
return (<>
|
|
34
|
-
{state.portals.map(({ key, children }
|
|
35
|
-
style={[StyleSheet.absoluteFill, { zIndex: 1000 + i, pointerEvents: 'box-none' }]}>
|
|
33
|
+
{state.portals.map(({ key, children }) => (<Fragment key={key}>
|
|
36
34
|
{children}
|
|
37
|
-
</
|
|
35
|
+
</Fragment>))}
|
|
38
36
|
</>);
|
|
39
37
|
});
|
|
40
38
|
export default _PortalManager;
|