@tarojs/components-advanced 4.1.12-beta.4 → 4.1.12-beta.40
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/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/list/hooks/useItemSizeCache.d.ts +1 -13
- package/dist/components/list/hooks/useItemSizeCache.js +6 -50
- package/dist/components/list/hooks/useItemSizeCache.js.map +1 -1
- package/dist/components/list/hooks/useListNestedScroll.d.ts +18 -0
- package/dist/components/list/hooks/useListNestedScroll.js +61 -0
- package/dist/components/list/hooks/useListNestedScroll.js.map +1 -0
- package/dist/components/list/hooks/useListScrollElementAttach.d.ts +25 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js +88 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js.map +1 -0
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.d.ts +14 -0
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js +134 -0
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js.map +1 -0
- package/dist/components/list/hooks/useMeasureStartOffset.d.ts +12 -0
- package/dist/components/list/hooks/useMeasureStartOffset.js +84 -0
- package/dist/components/list/hooks/useMeasureStartOffset.js.map +1 -0
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.d.ts +14 -0
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.js +82 -0
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.js.map +1 -0
- package/dist/components/list/hooks/useRefresher.d.ts +10 -2
- package/dist/components/list/hooks/useRefresher.js +150 -69
- package/dist/components/list/hooks/useRefresher.js.map +1 -1
- package/dist/components/list/hooks/useResizeObserver.d.ts +1 -0
- package/dist/components/list/hooks/useResizeObserver.js +37 -31
- package/dist/components/list/hooks/useResizeObserver.js.map +1 -1
- package/dist/components/list/hooks/useScrollCorrection.d.ts +3 -2
- package/dist/components/list/hooks/useScrollCorrection.js +7 -5
- package/dist/components/list/hooks/useScrollCorrection.js.map +1 -1
- package/dist/components/list/hooks/useScrollParentAutoFind.d.ts +20 -0
- package/dist/components/list/hooks/useScrollParentAutoFind.js +81 -0
- package/dist/components/list/hooks/useScrollParentAutoFind.js.map +1 -0
- package/dist/components/list/index.d.ts +16 -6
- package/dist/components/list/index.js +435 -498
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/list/utils.d.ts +5 -0
- package/dist/components/list/utils.js +17 -1
- package/dist/components/list/utils.js.map +1 -1
- package/dist/components/virtual-list/vue/list.d.ts +10 -10
- package/dist/components/virtual-waterfall/vue/waterfall.d.ts +10 -10
- package/dist/components/water-flow/flow-item.js +6 -4
- package/dist/components/water-flow/flow-item.js.map +1 -1
- package/dist/components/water-flow/flow-section.js +1 -1
- package/dist/components/water-flow/flow-section.js.map +1 -1
- package/dist/components/water-flow/index.d.ts +1 -1
- package/dist/components/water-flow/interface.d.ts +18 -2
- package/dist/components/water-flow/root.d.ts +35 -4
- package/dist/components/water-flow/root.js +114 -42
- package/dist/components/water-flow/root.js.map +1 -1
- package/dist/components/water-flow/section.d.ts +7 -1
- package/dist/components/water-flow/section.js +54 -9
- package/dist/components/water-flow/section.js.map +1 -1
- package/dist/components/water-flow/utils.d.ts +4 -0
- package/dist/components/water-flow/utils.js +5 -1
- package/dist/components/water-flow/utils.js.map +1 -1
- package/dist/components/water-flow/water-flow-node-cache.d.ts +24 -0
- package/dist/components/water-flow/water-flow-node-cache.js +161 -0
- package/dist/components/water-flow/water-flow-node-cache.js.map +1 -0
- package/dist/components/water-flow/water-flow.d.ts +2 -3
- package/dist/components/water-flow/water-flow.js +286 -31
- package/dist/components/water-flow/water-flow.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/scrollElementContext.d.ts +15 -0
- package/dist/utils/scrollElementContext.js +14 -0
- package/dist/utils/scrollElementContext.js.map +1 -0
- package/dist/utils/scrollParent.d.ts +33 -0
- package/dist/utils/scrollParent.js +88 -0
- package/dist/utils/scrollParent.js.map +1 -0
- package/package.json +9 -8
|
@@ -1,39 +1,65 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Slot, View } from '@tarojs/components';
|
|
3
|
-
import React, { useState, useRef, useCallback } from 'react';
|
|
3
|
+
import React, { useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
4
4
|
import { supportsNativeRefresher } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* 下拉刷新(List 内部)
|
|
8
8
|
*
|
|
9
|
-
* - 小程序:
|
|
10
|
-
* - H5
|
|
9
|
+
* - 小程序:ScrollView 原生 refresher-*;业务 Promise reject 时补发 Failed(4)(原生不派发)
|
|
10
|
+
* - H5:触顶 + touch 拖拽;最大行程 / 阻尼停拉线为视口高的 0.9 / 0.8(innerHeight);收尾仅依赖 onRefresherRefresh 的 Promise
|
|
11
|
+
* - 动画时间戳用 nowMs():小程序部分环境无 performance,避免抛错中断回弹
|
|
12
|
+
* - rafRef 与受控 refresherTriggered→false 的 effect 共用:effect 内 cancel 可能打断 H5 成功回弹,须在 effect 动画结束处释放 refreshingLockRef
|
|
13
|
+
* - H5 reject:onRefresherStatusChange 顺序 Failed(4) → Completed(3) → Idle(0)
|
|
14
|
+
* - emitStatusChange:仅 status 变化时回调(同 status 不重复)
|
|
11
15
|
*/
|
|
12
16
|
const BOUNCE_MS = 300;
|
|
13
17
|
const AT_TOP_THRESHOLD = 3;
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const DAMPING = 100;
|
|
18
|
-
/** 角度上限(度),小于此值才视为下拉意图 */
|
|
18
|
+
const H5_PULL_DISTANCE_MAX_VH = 0.9;
|
|
19
|
+
const H5_DAMPING_LIMIT_VH = 0.8;
|
|
20
|
+
const VIEWPORT_HEIGHT_FALLBACK = 800;
|
|
19
21
|
const DEG_LIMIT = 40;
|
|
20
|
-
/** 默认刷新层高度(对齐 Dynamic),无自定义 children 时使用 */
|
|
21
22
|
const DEFAULT_REFRESHER_HEIGHT = 50;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
function resolveTaroMeasureElement(ref) {
|
|
24
|
+
var _a;
|
|
25
|
+
const n = ref.current;
|
|
26
|
+
if (!n)
|
|
27
|
+
return null;
|
|
28
|
+
if (typeof Element !== 'undefined' && n instanceof Element)
|
|
29
|
+
return n;
|
|
30
|
+
const el = (_a = n.$el) !== null && _a !== void 0 ? _a : n;
|
|
31
|
+
if (el && typeof Element !== 'undefined' && el instanceof Element)
|
|
32
|
+
return el;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function nowMs() {
|
|
36
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
37
|
+
return performance.now();
|
|
38
|
+
}
|
|
39
|
+
return Date.now();
|
|
40
|
+
}
|
|
41
|
+
function getViewportHeightPx() {
|
|
42
|
+
if (typeof window === 'undefined' || !Number.isFinite(window.innerHeight) || window.innerHeight <= 0) {
|
|
43
|
+
return VIEWPORT_HEIGHT_FALLBACK;
|
|
44
|
+
}
|
|
45
|
+
return window.innerHeight;
|
|
46
|
+
}
|
|
47
|
+
/** 阻尼增量:ratio=总下拉位移/视口高;超过 dampingLimit 后不再累加 */
|
|
48
|
+
function dampIncrement(diff, totalMove, currentPull, viewportHeight, dampingLimit) {
|
|
27
49
|
if (diff <= 0)
|
|
28
50
|
return 0;
|
|
29
|
-
if (currentPull >=
|
|
51
|
+
if (currentPull >= dampingLimit)
|
|
30
52
|
return 0;
|
|
31
|
-
const ratio = Math.min(totalMove /
|
|
53
|
+
const ratio = Math.min(totalMove / viewportHeight, 1);
|
|
32
54
|
return diff * (1 - ratio) * 0.6;
|
|
33
55
|
}
|
|
34
56
|
function useRefresher(config,
|
|
35
57
|
/** 列表逻辑顶部对应的 scrollTop,用于触顶判断;H5「顶栏悬浮+只滚列表」时传 0,imperative 内以 DOM scrollTop 为准 */
|
|
36
|
-
scrollTopAtLogicalTop
|
|
58
|
+
scrollTopAtLogicalTop,
|
|
59
|
+
/** scrollElement 模式下可选:下拉起始点须在 List 内才触发刷新;未传时默认 true(沿用原逻辑) */
|
|
60
|
+
getIsTouchInListArea,
|
|
61
|
+
/** H5 等:开启刷新区时是否测量自定义内容高度(与 List 侧 isH5 && refresher 一致) */
|
|
62
|
+
enableH5SlotMeasure) {
|
|
37
63
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
38
64
|
const [internalRefreshing, setInternalRefreshing] = useState(false);
|
|
39
65
|
const [pullDistance, setPullDistance] = useState(0);
|
|
@@ -59,7 +85,6 @@ scrollTopAtLogicalTop) {
|
|
|
59
85
|
thresholdRef.current = (_c = config === null || config === void 0 ? void 0 : config.refresherThreshold) !== null && _c !== void 0 ? _c : 45;
|
|
60
86
|
const configRef = useRef(config);
|
|
61
87
|
configRef.current = config;
|
|
62
|
-
/** H5:触发 onRefresherStatusChange 回调(仅状态变化时触发) */
|
|
63
88
|
const emitStatusChangeRef = useRef((status, dy) => {
|
|
64
89
|
var _a, _b;
|
|
65
90
|
if (refreshStatusRef.current !== status) {
|
|
@@ -74,9 +99,52 @@ scrollTopAtLogicalTop) {
|
|
|
74
99
|
const topThresholdPx = scrollTopWhenAtTop + AT_TOP_THRESHOLD;
|
|
75
100
|
const topThresholdPxRef = useRef(topThresholdPx);
|
|
76
101
|
topThresholdPxRef.current = topThresholdPx;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
const getIsTouchInListAreaRef = useRef(getIsTouchInListArea);
|
|
103
|
+
getIsTouchInListAreaRef.current = getIsTouchInListArea;
|
|
104
|
+
const [measuredSlotHeight, setMeasuredSlotHeight] = useState(0);
|
|
105
|
+
const h5MeasureRef = useRef(null);
|
|
106
|
+
const setH5MeasureRef = useCallback((el) => {
|
|
107
|
+
h5MeasureRef.current = el;
|
|
108
|
+
}, []);
|
|
109
|
+
const hasCustomRefresherChildren = (config === null || config === void 0 ? void 0 : config.children) != null;
|
|
110
|
+
const slotHeight = useMemo(() => {
|
|
111
|
+
if (!config || config.refresherEnabled === false)
|
|
112
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
113
|
+
if (!hasCustomRefresherChildren)
|
|
114
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
115
|
+
if (supportsNativeRefresher)
|
|
116
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
117
|
+
if (!enableH5SlotMeasure)
|
|
118
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
119
|
+
return measuredSlotHeight > 0 ? measuredSlotHeight : DEFAULT_REFRESHER_HEIGHT;
|
|
120
|
+
}, [
|
|
121
|
+
config,
|
|
122
|
+
hasCustomRefresherChildren,
|
|
123
|
+
measuredSlotHeight,
|
|
124
|
+
enableH5SlotMeasure,
|
|
125
|
+
]);
|
|
126
|
+
const slotHeightRef = useRef(slotHeight);
|
|
127
|
+
slotHeightRef.current = slotHeight;
|
|
128
|
+
useLayoutEffect(() => {
|
|
129
|
+
if (!enableH5SlotMeasure) {
|
|
130
|
+
setMeasuredSlotHeight(0);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!hasCustomRefresherChildren)
|
|
134
|
+
return;
|
|
135
|
+
const node = resolveTaroMeasureElement(h5MeasureRef);
|
|
136
|
+
if (!node || typeof ResizeObserver === 'undefined')
|
|
137
|
+
return;
|
|
138
|
+
const ro = new ResizeObserver((entries) => {
|
|
139
|
+
const entry = entries[0];
|
|
140
|
+
if (!entry)
|
|
141
|
+
return;
|
|
142
|
+
const h = Math.max(1, Math.ceil(entry.contentRect.height));
|
|
143
|
+
setMeasuredSlotHeight(prev => (prev === h ? prev : h));
|
|
144
|
+
});
|
|
145
|
+
ro.observe(node);
|
|
146
|
+
return () => ro.disconnect();
|
|
147
|
+
}, [enableH5SlotMeasure, hasCustomRefresherChildren, config === null || config === void 0 ? void 0 : config.children]);
|
|
80
148
|
const scrollViewRefresherProps = config && supportsNativeRefresher ? {
|
|
81
149
|
refresherEnabled: (_d = config.refresherEnabled) !== null && _d !== void 0 ? _d : true,
|
|
82
150
|
refresherThreshold: (_e = config.refresherThreshold) !== null && _e !== void 0 ? _e : 45,
|
|
@@ -90,12 +158,16 @@ scrollTopAtLogicalTop) {
|
|
|
90
158
|
(_a = config.onRefresherPulling) === null || _a === void 0 ? void 0 : _a.call(config, { detail: { deltaY: (_c = (_b = e.detail) === null || _b === void 0 ? void 0 : _b.deltaY) !== null && _c !== void 0 ? _c : 0 } });
|
|
91
159
|
},
|
|
92
160
|
onRefresherRefresh: async () => {
|
|
93
|
-
var _a;
|
|
161
|
+
var _a, _b;
|
|
94
162
|
if (!isControlled)
|
|
95
163
|
setInternalRefreshing(true);
|
|
96
164
|
try {
|
|
97
165
|
await ((_a = config.onRefresherRefresh) === null || _a === void 0 ? void 0 : _a.call(config));
|
|
98
166
|
}
|
|
167
|
+
catch (_c) {
|
|
168
|
+
// 原生不派发 Failed(4),补发便于与 H5 一致
|
|
169
|
+
(_b = config.onRefresherStatusChange) === null || _b === void 0 ? void 0 : _b.call(config, { detail: { status: 4 /* RefreshStatus.Failed */, dy: 0 } });
|
|
170
|
+
}
|
|
99
171
|
finally {
|
|
100
172
|
if (!isControlled)
|
|
101
173
|
setInternalRefreshing(false);
|
|
@@ -111,11 +183,7 @@ scrollTopAtLogicalTop) {
|
|
|
111
183
|
(_a = config.onRefresherStatusChange) === null || _a === void 0 ? void 0 : _a.call(config, { detail: { status: (_b = e.detail) === null || _b === void 0 ? void 0 : _b.status, dy: (_c = e.detail) === null || _c === void 0 ? void 0 : _c.dy } });
|
|
112
184
|
},
|
|
113
185
|
} : {};
|
|
114
|
-
// ========================================
|
|
115
|
-
// H5:单一结构 + 原生 touch 监听(passive: false);refresherEnabled=false 时不挂
|
|
116
|
-
// ========================================
|
|
117
186
|
const addImperativeTouchListeners = React.useCallback((el) => {
|
|
118
|
-
var _a, _b;
|
|
119
187
|
if (supportsNativeRefresher || !configRef.current || !h5RefresherEnabled)
|
|
120
188
|
return () => { };
|
|
121
189
|
const scrollEl = el;
|
|
@@ -123,12 +191,13 @@ scrollTopAtLogicalTop) {
|
|
|
123
191
|
const startX = { current: 0 };
|
|
124
192
|
const lastY = { current: 0 };
|
|
125
193
|
let touchStartedAtTop = false;
|
|
194
|
+
/** 下拉起始点是否在 List 内(scrollElement 模式);未传 getIsTouchInListArea 时恒为 true */
|
|
195
|
+
let touchStartedInListArea = true;
|
|
126
196
|
let dragOnEdge = false;
|
|
127
197
|
let lastPull = 0;
|
|
128
198
|
/** 用于 onTouchEnd 判断是否触发刷新;刷新完成后必须置 0,否则下次点击(无 touchMove)会误用上次的 lastPull 再次触发 */
|
|
129
199
|
const lastPullRef = { current: 0 };
|
|
130
200
|
const pullAtReleaseRef = { current: 0 };
|
|
131
|
-
const screenHeight = typeof window !== 'undefined' ? (_b = (_a = window.screen) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 600 : 600;
|
|
132
201
|
const setPull = (v) => {
|
|
133
202
|
lastPull = v;
|
|
134
203
|
lastPullRef.current = v;
|
|
@@ -137,9 +206,9 @@ scrollTopAtLogicalTop) {
|
|
|
137
206
|
const runBounceBack = (fromValue, onComplete) => {
|
|
138
207
|
if (rafRef.current != null)
|
|
139
208
|
cancelAnimationFrame(rafRef.current);
|
|
140
|
-
const startTime =
|
|
209
|
+
const startTime = nowMs();
|
|
141
210
|
const animate = () => {
|
|
142
|
-
const t = Math.min((
|
|
211
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
143
212
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
144
213
|
const v = fromValue * (1 - ease);
|
|
145
214
|
setPullDistanceRef.current(v);
|
|
@@ -150,7 +219,6 @@ scrollTopAtLogicalTop) {
|
|
|
150
219
|
}
|
|
151
220
|
else {
|
|
152
221
|
setPullDistanceRef.current(0);
|
|
153
|
-
// 修复闭包问题:使用 ref 获取最新值
|
|
154
222
|
if (!isControlledRef.current)
|
|
155
223
|
setInternalRefreshingRef.current(false);
|
|
156
224
|
rafRef.current = null;
|
|
@@ -163,10 +231,10 @@ scrollTopAtLogicalTop) {
|
|
|
163
231
|
const runBounceBackAbort = (fromValue) => {
|
|
164
232
|
if (rafRef.current != null)
|
|
165
233
|
cancelAnimationFrame(rafRef.current);
|
|
166
|
-
const startTime =
|
|
234
|
+
const startTime = nowMs();
|
|
167
235
|
const animate = () => {
|
|
168
236
|
var _a, _b;
|
|
169
|
-
const t = Math.min((
|
|
237
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
170
238
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
171
239
|
const v = fromValue * (1 - ease);
|
|
172
240
|
setPullDistanceRef.current(v);
|
|
@@ -180,19 +248,18 @@ scrollTopAtLogicalTop) {
|
|
|
180
248
|
lastPullRef.current = 0;
|
|
181
249
|
lastPull = 0;
|
|
182
250
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherAbort) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
183
|
-
// 状态变化:回到 Idle
|
|
184
251
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
185
252
|
}
|
|
186
253
|
};
|
|
187
254
|
rafRef.current = requestAnimationFrame(animate);
|
|
188
255
|
};
|
|
189
|
-
/**
|
|
256
|
+
/** 收起到加载高度后再执行 onRefresherRefresh */
|
|
190
257
|
const runBounceToLoading = (fromValue, toValue, onReach) => {
|
|
191
258
|
if (rafRef.current != null)
|
|
192
259
|
cancelAnimationFrame(rafRef.current);
|
|
193
|
-
const startTime =
|
|
260
|
+
const startTime = nowMs();
|
|
194
261
|
const animate = () => {
|
|
195
|
-
const t = Math.min((
|
|
262
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
196
263
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
197
264
|
setPullDistanceRef.current(fromValue + (toValue - fromValue) * ease);
|
|
198
265
|
if (t < 1) {
|
|
@@ -208,6 +275,7 @@ scrollTopAtLogicalTop) {
|
|
|
208
275
|
};
|
|
209
276
|
const isEdge = () => { var _a; return ((_a = scrollEl.scrollTop) !== null && _a !== void 0 ? _a : 0) <= topThresholdPxRef.current; };
|
|
210
277
|
const onTouchStart = (e) => {
|
|
278
|
+
var _a, _b;
|
|
211
279
|
if (refreshingLockRef.current || isRefreshingRef.current)
|
|
212
280
|
return;
|
|
213
281
|
if (rafRef.current != null) {
|
|
@@ -216,6 +284,7 @@ scrollTopAtLogicalTop) {
|
|
|
216
284
|
}
|
|
217
285
|
lastPull = lastPullRef.current;
|
|
218
286
|
touchStartedAtTop = isEdge();
|
|
287
|
+
touchStartedInListArea = (_b = (_a = getIsTouchInListAreaRef.current) === null || _a === void 0 ? void 0 : _a.call(getIsTouchInListAreaRef, e)) !== null && _b !== void 0 ? _b : true;
|
|
219
288
|
const t0 = e.touches[0];
|
|
220
289
|
if (t0) {
|
|
221
290
|
startY.current = t0.clientY;
|
|
@@ -227,16 +296,29 @@ scrollTopAtLogicalTop) {
|
|
|
227
296
|
var _a, _b, _c, _d;
|
|
228
297
|
if (refreshingLockRef.current || isRefreshingRef.current)
|
|
229
298
|
return;
|
|
299
|
+
if (!touchStartedInListArea)
|
|
300
|
+
return;
|
|
230
301
|
const touch = ev.touches[0];
|
|
231
302
|
if (!touch)
|
|
232
303
|
return;
|
|
233
304
|
const currentY = touch.clientY;
|
|
234
305
|
const currentX = touch.clientX;
|
|
235
|
-
if (!
|
|
306
|
+
if (!isEdge()) {
|
|
236
307
|
setPull(0);
|
|
237
308
|
dragOnEdge = false;
|
|
238
309
|
return;
|
|
239
310
|
}
|
|
311
|
+
// 到顶后:若 touch 开始时不在顶部,在首次下拉(dy>0)时「采纳」该手势,使同一手势内滚到顶后继续下拉可触发刷新
|
|
312
|
+
if (!touchStartedAtTop && !dragOnEdge) {
|
|
313
|
+
const dyCheck = currentY - lastY.current;
|
|
314
|
+
if (dyCheck <= 0)
|
|
315
|
+
return;
|
|
316
|
+
touchStartedAtTop = true;
|
|
317
|
+
// 重置起点,避免采纳时 dy 过大导致刷新层瞬间拉满
|
|
318
|
+
startY.current = currentY;
|
|
319
|
+
startX.current = currentX;
|
|
320
|
+
lastY.current = currentY;
|
|
321
|
+
}
|
|
240
322
|
const dy = currentY - lastY.current;
|
|
241
323
|
// 还没进入下拉拖拽阶段(dragOnEdge=false)时,先看第一下是往上还是往下:
|
|
242
324
|
// - 第一笔就是往上滑(dy<=0):视为正常滚动,直接交给原生 Scroll,不拦截、不启动下拉逻辑
|
|
@@ -271,8 +353,11 @@ scrollTopAtLogicalTop) {
|
|
|
271
353
|
return;
|
|
272
354
|
}
|
|
273
355
|
lastY.current = currentY;
|
|
274
|
-
const
|
|
275
|
-
const
|
|
356
|
+
const vh = getViewportHeightPx();
|
|
357
|
+
const maxPull = vh * H5_PULL_DISTANCE_MAX_VH;
|
|
358
|
+
const dampingLimit = vh * H5_DAMPING_LIMIT_VH;
|
|
359
|
+
const damped = dampIncrement(dy, totalMove, lastPull, vh, dampingLimit);
|
|
360
|
+
const next = Math.min(lastPull + damped, maxPull);
|
|
276
361
|
setPull(next);
|
|
277
362
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherPulling) === null || _b === void 0 ? void 0 : _b.call(_a, { detail: { deltaY: next } });
|
|
278
363
|
if (next >= thresholdRef.current) {
|
|
@@ -289,11 +374,10 @@ scrollTopAtLogicalTop) {
|
|
|
289
374
|
return;
|
|
290
375
|
refreshingLockRef.current = true;
|
|
291
376
|
const pullValue = lastPull;
|
|
292
|
-
const height =
|
|
377
|
+
const height = slotHeightRef.current;
|
|
293
378
|
pullAtReleaseRef.current = pullValue;
|
|
294
379
|
if (!isControlledRef.current)
|
|
295
380
|
setInternalRefreshingRef.current(true);
|
|
296
|
-
// 状态变化:Refreshing
|
|
297
381
|
emitStatusChangeRef.current(2 /* RefreshStatus.Refreshing */, pullValue);
|
|
298
382
|
runBounceToLoading(pullValue, height, () => {
|
|
299
383
|
var _a, _b;
|
|
@@ -307,30 +391,26 @@ scrollTopAtLogicalTop) {
|
|
|
307
391
|
lastPull = 0;
|
|
308
392
|
pullAtReleaseRef.current = 0;
|
|
309
393
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRestore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
310
|
-
// 状态变化:回到 Idle
|
|
311
394
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
312
395
|
};
|
|
313
|
-
const timeoutId = setTimeout(safeReset, BOUNCE_MS + 400);
|
|
314
396
|
Promise.resolve((_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRefresh) === null || _b === void 0 ? void 0 : _b.call(_a))
|
|
315
397
|
.then(() => {
|
|
316
|
-
// 若已经通过 safeReset 提前结束刷新(例如超时),则不再执行回弹动画,避免二次「吐舌头」闪现
|
|
317
398
|
if (!refreshingLockRef.current)
|
|
318
399
|
return;
|
|
319
|
-
|
|
320
|
-
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */,
|
|
321
|
-
requestAnimationFrame(() => runBounceBack(
|
|
400
|
+
const slotH = slotHeightRef.current;
|
|
401
|
+
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, slotH);
|
|
402
|
+
requestAnimationFrame(() => runBounceBack(slotH, () => {
|
|
322
403
|
var _a, _b;
|
|
323
|
-
clearTimeout(timeoutId);
|
|
324
404
|
refreshingLockRef.current = false;
|
|
325
405
|
lastPullRef.current = 0;
|
|
326
406
|
lastPull = 0;
|
|
327
407
|
pullAtReleaseRef.current = 0;
|
|
328
408
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRestore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
329
|
-
// 状态变化:回到 Idle
|
|
330
409
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
331
410
|
}));
|
|
332
411
|
}, () => {
|
|
333
|
-
|
|
412
|
+
emitStatusChangeRef.current(4 /* RefreshStatus.Failed */, pullValue);
|
|
413
|
+
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, slotHeightRef.current);
|
|
334
414
|
safeReset();
|
|
335
415
|
});
|
|
336
416
|
});
|
|
@@ -338,6 +418,8 @@ scrollTopAtLogicalTop) {
|
|
|
338
418
|
const onTouchEnd = () => {
|
|
339
419
|
if (refreshingLockRef.current)
|
|
340
420
|
return;
|
|
421
|
+
if (!touchStartedInListArea)
|
|
422
|
+
return;
|
|
341
423
|
dragOnEdge = false;
|
|
342
424
|
const current = lastPullRef.current;
|
|
343
425
|
if (current >= thresholdRef.current) {
|
|
@@ -400,7 +482,7 @@ scrollTopAtLogicalTop) {
|
|
|
400
482
|
const defaultText = showDefaultText
|
|
401
483
|
? (isRefreshing ? '加载中...' : pullDistance >= threshold ? '释放刷新' : '下拉刷新')
|
|
402
484
|
: null;
|
|
403
|
-
return (jsx(View, { style: {
|
|
485
|
+
return (jsx(View, { ref: !supportsNativeRefresher && hasCustomChildren ? setH5MeasureRef : undefined, style: {
|
|
404
486
|
width: '100%',
|
|
405
487
|
minHeight: hasCustomChildren ? undefined : DEFAULT_REFRESHER_HEIGHT,
|
|
406
488
|
height: hasCustomChildren ? 'auto' : DEFAULT_REFRESHER_HEIGHT,
|
|
@@ -409,20 +491,20 @@ scrollTopAtLogicalTop) {
|
|
|
409
491
|
justifyContent: 'center',
|
|
410
492
|
alignItems: 'center',
|
|
411
493
|
background,
|
|
412
|
-
},
|
|
413
|
-
}, [config, pullDistance, isRefreshing]);
|
|
414
|
-
|
|
494
|
+
}, children: hasCustomChildren ? config.children : (defaultText ? jsx(View, { style: textColor ? { color: textColor } : undefined, children: defaultText }) : null) }));
|
|
495
|
+
}, [config, pullDistance, isRefreshing, setH5MeasureRef]);
|
|
496
|
+
/** 受控 refresherTriggered=true:无下拉也显示加载条 */
|
|
415
497
|
React.useEffect(() => {
|
|
416
498
|
if (!isControlled || !config || config.refresherTriggered !== true)
|
|
417
499
|
return;
|
|
418
|
-
|
|
500
|
+
const h = slotHeightRef.current;
|
|
501
|
+
if (pullDistanceRef.current >= h)
|
|
419
502
|
return;
|
|
420
|
-
setPullDistanceRef.current(
|
|
421
|
-
pullDistanceRef.current =
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
// 受控 refresherTriggered:父组件设为 false 时同步回弹并清零
|
|
503
|
+
setPullDistanceRef.current(h);
|
|
504
|
+
pullDistanceRef.current = h;
|
|
505
|
+
emitStatusChangeRef.current(2 /* RefreshStatus.Refreshing */, h);
|
|
506
|
+
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered, slotHeight]);
|
|
507
|
+
/** 受控 refresherTriggered=false:回弹清零;与 touch 共用 rafRef,cancel 时须在此释放 refreshingLockRef */
|
|
426
508
|
const prevTriggeredRef = useRef(config === null || config === void 0 ? void 0 : config.refresherTriggered);
|
|
427
509
|
React.useEffect(() => {
|
|
428
510
|
if (!isControlled || !config || config.refresherTriggered !== false) {
|
|
@@ -434,13 +516,12 @@ scrollTopAtLogicalTop) {
|
|
|
434
516
|
if (prev === true && (pullDistanceRef.current > 0 || isRefreshingRef.current)) {
|
|
435
517
|
if (rafRef.current != null)
|
|
436
518
|
cancelAnimationFrame(rafRef.current);
|
|
437
|
-
const from = pullDistanceRef.current > 0 ? pullDistanceRef.current :
|
|
438
|
-
// 触发 Completed 状态变化
|
|
519
|
+
const from = pullDistanceRef.current > 0 ? pullDistanceRef.current : slotHeightRef.current;
|
|
439
520
|
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, from);
|
|
440
|
-
const startTime =
|
|
521
|
+
const startTime = nowMs();
|
|
441
522
|
const animate = () => {
|
|
442
523
|
var _a;
|
|
443
|
-
const t = Math.min((
|
|
524
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
444
525
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
445
526
|
const v = from * (1 - ease);
|
|
446
527
|
setPullDistanceRef.current(v);
|
|
@@ -450,18 +531,17 @@ scrollTopAtLogicalTop) {
|
|
|
450
531
|
else {
|
|
451
532
|
setPullDistanceRef.current(0);
|
|
452
533
|
pullDistanceRef.current = 0;
|
|
453
|
-
// 修复闭包问题:使用 ref 获取最新值
|
|
454
534
|
if (!isControlledRef.current)
|
|
455
535
|
setInternalRefreshingRef.current(false);
|
|
456
536
|
rafRef.current = null;
|
|
537
|
+
refreshingLockRef.current = false;
|
|
457
538
|
(_a = config.onRefresherRestore) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
458
|
-
// 触发 Idle 状态变化
|
|
459
539
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
460
540
|
}
|
|
461
541
|
};
|
|
462
542
|
rafRef.current = requestAnimationFrame(animate);
|
|
463
543
|
}
|
|
464
|
-
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered]);
|
|
544
|
+
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered, slotHeight]);
|
|
465
545
|
return {
|
|
466
546
|
scrollViewRefresherProps,
|
|
467
547
|
scrollViewRefresherHandlers,
|
|
@@ -473,6 +553,7 @@ scrollTopAtLogicalTop) {
|
|
|
473
553
|
},
|
|
474
554
|
addImperativeTouchListeners: !supportsNativeRefresher && config && h5RefresherEnabled ? addImperativeTouchListeners : undefined,
|
|
475
555
|
renderRefresherContent,
|
|
556
|
+
slotHeight,
|
|
476
557
|
};
|
|
477
558
|
}
|
|
478
559
|
|