@mpxjs/webpack-plugin 2.10.3-beta.17 → 2.10.3-beta.18
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/context.js +5 -1
- package/lib/runtime/components/react/dist/event.config.js +0 -1
- package/lib/runtime/components/react/dist/getInnerListeners.js +148 -149
- package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +11 -7
- package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
- package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +23 -21
- package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +9 -4
- package/lib/runtime/components/react/dist/mpx-checkbox.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/index.jsx +9 -4
- package/lib/runtime/components/react/dist/mpx-image.jsx +92 -41
- package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
- package/lib/runtime/components/react/dist/mpx-input.jsx +14 -13
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +22 -7
- package/lib/runtime/components/react/dist/mpx-label.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +10 -5
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +206 -80
- package/lib/runtime/components/react/dist/mpx-navigator.jsx +11 -3
- package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
- package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
- package/lib/runtime/components/react/dist/mpx-picker/index.jsx +178 -98
- package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
- package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
- package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
- package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +16 -15
- package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +95 -26
- package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +16 -16
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
- package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
- package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
- package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
- package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
- 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 +11 -4
- package/lib/runtime/components/react/dist/mpx-radio.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +12 -4
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +317 -89
- package/lib/runtime/components/react/dist/mpx-simple-text.jsx +7 -5
- package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
- package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
- package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
- package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +15 -14
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +245 -121
- package/lib/runtime/components/react/dist/mpx-switch.jsx +10 -7
- package/lib/runtime/components/react/dist/mpx-text.jsx +43 -13
- package/lib/runtime/components/react/dist/mpx-video.jsx +12 -7
- package/lib/runtime/components/react/dist/mpx-view.jsx +34 -18
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +40 -35
- package/lib/runtime/components/react/dist/useAnimationHooks.js +35 -90
- package/lib/runtime/components/react/dist/utils.jsx +215 -109
- package/lib/runtime/components/web/mpx-titlebar.vue +21 -18
- package/package.json +1 -1
- /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
- /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
- /package/lib/runtime/components/react/dist/{pickerViewMask.jsx → mpx-picker-view-column/pickerViewMask.jsx} +0 -0
|
@@ -1,22 +1,31 @@
|
|
|
1
|
-
import React, { useContext, useEffect } from 'react';
|
|
2
|
-
import { Keyboard, View
|
|
3
|
-
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
1
|
+
import React, { useContext, useEffect, useRef } from 'react';
|
|
2
|
+
import { Keyboard, View } from 'react-native';
|
|
3
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, cancelAnimation } from 'react-native-reanimated';
|
|
4
4
|
import { KeyboardAvoidContext } from './context';
|
|
5
|
+
import { isIOS } from './utils';
|
|
5
6
|
const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
6
|
-
const isIOS = Platform.OS === 'ios';
|
|
7
7
|
const duration = isIOS ? 250 : 300;
|
|
8
8
|
const easing = isIOS ? Easing.inOut(Easing.ease) : Easing.out(Easing.quad);
|
|
9
9
|
const offset = useSharedValue(0);
|
|
10
10
|
const basic = useSharedValue('auto');
|
|
11
11
|
const keyboardAvoid = useContext(KeyboardAvoidContext);
|
|
12
|
+
// fix: 某些特殊机型下隐藏键盘可能会先触发一次 keyboardWillShow,
|
|
13
|
+
// 比如机型 iPhone 11 Pro,可能会导致显隐动画冲突
|
|
14
|
+
// 因此增加状态标记 + cancelAnimation 来优化
|
|
15
|
+
const isShow = useRef(false);
|
|
12
16
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
13
17
|
transform: [{ translateY: -offset.value }],
|
|
14
18
|
flexBasis: basic.value
|
|
15
19
|
}));
|
|
16
20
|
const resetKeyboard = () => {
|
|
21
|
+
if (!isShow.current) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
isShow.current = false;
|
|
17
25
|
if (keyboardAvoid?.current) {
|
|
18
26
|
keyboardAvoid.current = null;
|
|
19
27
|
}
|
|
28
|
+
cancelAnimation(offset);
|
|
20
29
|
offset.value = withTiming(0, { duration, easing });
|
|
21
30
|
basic.value = 'auto';
|
|
22
31
|
};
|
|
@@ -30,16 +39,19 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
30
39
|
if (isIOS) {
|
|
31
40
|
subscriptions = [
|
|
32
41
|
Keyboard.addListener('keyboardWillShow', (evt) => {
|
|
33
|
-
if (!keyboardAvoid?.current)
|
|
42
|
+
if (!keyboardAvoid?.current || isShow.current) {
|
|
34
43
|
return;
|
|
44
|
+
}
|
|
45
|
+
isShow.current = true;
|
|
35
46
|
const { endCoordinates } = evt;
|
|
36
47
|
const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
|
|
37
48
|
setTimeout(() => {
|
|
38
49
|
ref?.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
39
|
-
const aboveOffset = pageY + height - endCoordinates.screenY;
|
|
50
|
+
const aboveOffset = offset.value + pageY + height - endCoordinates.screenY;
|
|
40
51
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
41
52
|
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
|
|
42
53
|
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
54
|
+
cancelAnimation(offset);
|
|
43
55
|
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
44
56
|
if (finished) {
|
|
45
57
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
@@ -55,8 +67,10 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
55
67
|
else {
|
|
56
68
|
subscriptions = [
|
|
57
69
|
Keyboard.addListener('keyboardDidShow', (evt) => {
|
|
58
|
-
if (!keyboardAvoid?.current)
|
|
70
|
+
if (!keyboardAvoid?.current || isShow.current) {
|
|
59
71
|
return;
|
|
72
|
+
}
|
|
73
|
+
isShow.current = true;
|
|
60
74
|
const { endCoordinates } = evt;
|
|
61
75
|
const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
|
|
62
76
|
ref?.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
@@ -65,6 +79,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
65
79
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
66
80
|
const belowValue = Math.min(belowOffset, cursorSpacing);
|
|
67
81
|
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
82
|
+
cancelAnimation(offset);
|
|
68
83
|
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
69
84
|
if (finished) {
|
|
70
85
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
@@ -8,6 +8,7 @@ import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
|
8
8
|
import useNodesRef from './useNodesRef';
|
|
9
9
|
import { splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject } from './utils';
|
|
10
10
|
import { LabelContext } from './context';
|
|
11
|
+
import Portal from './mpx-portal';
|
|
11
12
|
const Label = forwardRef((labelProps, ref) => {
|
|
12
13
|
const { textProps, innerProps: props = {} } = splitProps(labelProps);
|
|
13
14
|
const propsRef = useRef({});
|
|
@@ -17,7 +18,7 @@ const Label = forwardRef((labelProps, ref) => {
|
|
|
17
18
|
flexDirection: 'row'
|
|
18
19
|
};
|
|
19
20
|
const styleObj = extendObject({}, defaultStyle, style);
|
|
20
|
-
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
21
|
+
const { hasPositionFixed, hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
21
22
|
const nodeRef = useRef(null);
|
|
22
23
|
useNodesRef(props, ref, nodeRef, { style: normalStyle });
|
|
23
24
|
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
|
|
@@ -33,20 +34,23 @@ const Label = forwardRef((labelProps, ref) => {
|
|
|
33
34
|
bindtap && bindtap(getCustomEvent('tap', evt, { layoutRef }, { props: propsRef.current }));
|
|
34
35
|
contextRef.current.triggerChange(evt);
|
|
35
36
|
}, []);
|
|
36
|
-
const innerProps = useInnerProps(props,
|
|
37
|
+
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
37
38
|
ref: nodeRef,
|
|
38
|
-
style: extendObject({}, innerStyle, layoutStyle)
|
|
39
|
-
}, layoutProps, {
|
|
39
|
+
style: extendObject({}, innerStyle, layoutStyle),
|
|
40
40
|
bindtap: onTap
|
|
41
41
|
}), [], {
|
|
42
42
|
layoutRef
|
|
43
43
|
});
|
|
44
|
-
|
|
44
|
+
const finalComponent = createElement(View, innerProps, createElement(LabelContext.Provider, { value: contextRef }, wrapChildren(props, {
|
|
45
45
|
hasVarDec,
|
|
46
46
|
varContext: varContextRef.current,
|
|
47
47
|
textStyle,
|
|
48
48
|
textProps
|
|
49
49
|
})));
|
|
50
|
+
if (hasPositionFixed) {
|
|
51
|
+
return createElement(Portal, null, finalComponent);
|
|
52
|
+
}
|
|
53
|
+
return finalComponent;
|
|
50
54
|
});
|
|
51
55
|
Label.displayName = 'MpxLabel';
|
|
52
56
|
export default Label;
|
|
@@ -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
|
|
@@ -19,14 +20,18 @@ const _MovableArea = forwardRef((props, ref) => {
|
|
|
19
20
|
width: normalStyle.width || 10
|
|
20
21
|
}), [normalStyle.width, normalStyle.height]);
|
|
21
22
|
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: movableViewRef });
|
|
22
|
-
const innerProps = useInnerProps(props,
|
|
23
|
-
style: extendObject({ height: contextValue.height, width: contextValue.width
|
|
23
|
+
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
24
|
+
style: extendObject({ height: contextValue.height, width: contextValue.width }, normalStyle, layoutStyle),
|
|
24
25
|
ref: movableViewRef
|
|
25
|
-
}
|
|
26
|
-
|
|
26
|
+
}), [], { layoutRef });
|
|
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
|
}
|
|
@@ -452,7 +578,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
452
578
|
'catchhtouchmove',
|
|
453
579
|
'catchtouchend'
|
|
454
580
|
]);
|
|
455
|
-
const innerProps = useInnerProps(filterProps,
|
|
581
|
+
const innerProps = useInnerProps(extendObject({}, filterProps, {
|
|
456
582
|
ref: nodeRef,
|
|
457
583
|
onLayout: onLayout,
|
|
458
584
|
style: [innerStyle, animatedStyles, layoutStyle]
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ✔ hover-class
|
|
3
|
+
* ✘ hover-stop-propagation
|
|
4
|
+
* ✔ hover-start-time
|
|
5
|
+
* ✔ hover-stay-time
|
|
6
|
+
* ✔ open-type
|
|
7
|
+
* ✔ url
|
|
8
|
+
* ✔ delta
|
|
9
|
+
*/
|
|
1
10
|
import { useCallback, forwardRef, createElement } from 'react';
|
|
2
|
-
import useInnerProps from './getInnerListeners';
|
|
3
11
|
import { redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy';
|
|
4
12
|
import MpxView from './mpx-view';
|
|
5
13
|
const _Navigator = forwardRef((props, ref) => {
|
|
@@ -23,10 +31,10 @@ const _Navigator = forwardRef((props, ref) => {
|
|
|
23
31
|
break;
|
|
24
32
|
}
|
|
25
33
|
}, [openType, url, delta]);
|
|
26
|
-
const innerProps =
|
|
34
|
+
const innerProps = {
|
|
27
35
|
ref,
|
|
28
36
|
bindtap: handleClick
|
|
29
|
-
}
|
|
37
|
+
};
|
|
30
38
|
return createElement(MpxView, innerProps, children);
|
|
31
39
|
});
|
|
32
40
|
_Navigator.displayName = 'MpxNavigator';
|