@tarojs/components-advanced 4.1.12-beta.13 → 4.1.12-beta.15
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/list/hooks/useItemSizeCache.d.ts +1 -1
- package/dist/components/list/hooks/useItemSizeCache.js +5 -5
- package/dist/components/list/hooks/useItemSizeCache.js.map +1 -1
- package/dist/components/list/hooks/useListScrollElementAttach.d.ts +4 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js +3 -1
- package/dist/components/list/hooks/useListScrollElementAttach.js.map +1 -1
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js +3 -4
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js.map +1 -1
- package/dist/components/list/index.d.ts +6 -5
- package/dist/components/list/index.js +65 -64
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/water-flow/index.d.ts +1 -1
- package/dist/components/water-flow/interface.d.ts +4 -0
- package/dist/components/water-flow/water-flow.d.ts +2 -3
- package/dist/components/water-flow/water-flow.js +13 -4
- package/dist/components/water-flow/water-flow.js.map +1 -1
- package/package.json +9 -9
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef, useCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
function useItemSizeCache(options) {
|
|
4
|
-
const {
|
|
4
|
+
const { estimatedSize } = options;
|
|
5
5
|
// 缓存 Map:key = 索引,value = 尺寸信息
|
|
6
6
|
const cacheRef = useRef(new Map());
|
|
7
7
|
/**
|
|
@@ -13,8 +13,8 @@ function useItemSizeCache(options) {
|
|
|
13
13
|
if ((cached === null || cached === void 0 ? void 0 : cached.isMeasured) && cached.measuredSize !== null) {
|
|
14
14
|
return cached.measuredSize;
|
|
15
15
|
}
|
|
16
|
-
return
|
|
17
|
-
}, [
|
|
16
|
+
return estimatedSize;
|
|
17
|
+
}, [estimatedSize]);
|
|
18
18
|
/**
|
|
19
19
|
* 设置项的尺寸(实际测量后调用)
|
|
20
20
|
*/
|
|
@@ -26,10 +26,10 @@ function useItemSizeCache(options) {
|
|
|
26
26
|
}
|
|
27
27
|
cacheRef.current.set(index, {
|
|
28
28
|
measuredSize: size,
|
|
29
|
-
estimatedSize
|
|
29
|
+
estimatedSize,
|
|
30
30
|
isMeasured: true
|
|
31
31
|
});
|
|
32
|
-
}, [
|
|
32
|
+
}, [estimatedSize]);
|
|
33
33
|
return {
|
|
34
34
|
getItemSize,
|
|
35
35
|
setItemSize
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useItemSizeCache.js","sources":["../../../../src/components/list/hooks/useItemSizeCache.ts"],"sourcesContent":["import { useCallback, useRef } from 'react'\n\n/**\n * useItemSizeCache Hook - 动态尺寸缓存管理\n *\n * 支持垂直滚动(高度)和水平滚动(宽度)两种模式\n */\n\n/** 单个项的尺寸缓存 */\ninterface ItemMeasureCache {\n measuredSize: number | null // 实际测量尺寸\n estimatedSize: number // 估算尺寸\n isMeasured: boolean // 是否已测量\n}\n\ninterface UseItemSizeCacheOptions {\n isHorizontal: boolean // 是否水平滚动\n
|
|
1
|
+
{"version":3,"file":"useItemSizeCache.js","sources":["../../../../src/components/list/hooks/useItemSizeCache.ts"],"sourcesContent":["import { useCallback, useRef } from 'react'\n\n/**\n * useItemSizeCache Hook - 动态尺寸缓存管理\n *\n * 支持垂直滚动(高度)和水平滚动(宽度)两种模式\n */\n\n/** 单个项的尺寸缓存 */\ninterface ItemMeasureCache {\n measuredSize: number | null // 实际测量尺寸\n estimatedSize: number // 估算尺寸\n isMeasured: boolean // 是否已测量\n}\n\ninterface UseItemSizeCacheOptions {\n isHorizontal: boolean // 是否水平滚动\n estimatedSize: number // 估算尺寸\n itemCount: number // 项总数\n}\n\ninterface UseItemSizeCacheReturn {\n /** 获取项的尺寸(高度或宽度) */\n getItemSize: (index: number) => number\n\n /** 设置项的尺寸 */\n setItemSize: (index: number, size: number) => void\n}\n\nexport function useItemSizeCache(\n options: UseItemSizeCacheOptions\n): UseItemSizeCacheReturn {\n const { estimatedSize } = options\n\n // 缓存 Map:key = 索引,value = 尺寸信息\n const cacheRef = useRef<Map<number, ItemMeasureCache>>(new Map())\n\n /**\n * 获取项的尺寸\n * 优先返回实际测量值,否则返回估算值\n */\n const getItemSize = useCallback((index: number): number => {\n const cached = cacheRef.current.get(index)\n\n if (cached?.isMeasured && cached.measuredSize !== null) {\n return cached.measuredSize\n }\n\n return estimatedSize\n }, [estimatedSize])\n\n /**\n * 设置项的尺寸(实际测量后调用)\n */\n const setItemSize = useCallback((index: number, size: number) => {\n const cached = cacheRef.current.get(index)\n\n // 尺寸变化小于 1px,忽略(避免微小抖动)\n if (cached?.measuredSize && Math.abs(cached.measuredSize - size) < 1) {\n return\n }\n\n cacheRef.current.set(index, {\n measuredSize: size,\n estimatedSize,\n isMeasured: true\n })\n }, [estimatedSize])\n\n return {\n getItemSize,\n setItemSize\n }\n}\n"],"names":[],"mappings":";;AA6BM,SAAU,gBAAgB,CAC9B,OAAgC,EAAA;AAEhC,IAAA,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO;;IAGjC,MAAM,QAAQ,GAAG,MAAM,CAAgC,IAAI,GAAG,EAAE,CAAC;AAEjE;;;AAGG;AACH,IAAA,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,KAAa,KAAY;QACxD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AAE1C,QAAA,IAAI,CAAA,MAAM,KAAN,IAAA,IAAA,MAAM,uBAAN,MAAM,CAAE,UAAU,KAAI,MAAM,CAAC,YAAY,KAAK,IAAI,EAAE;YACtD,OAAO,MAAM,CAAC,YAAY;;AAG5B,QAAA,OAAO,aAAa;AACtB,KAAC,EAAE,CAAC,aAAa,CAAC,CAAC;AAEnB;;AAEG;IACH,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,KAAa,EAAE,IAAY,KAAI;QAC9D,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;;QAG1C,IAAI,CAAA,MAAM,KAAN,IAAA,IAAA,MAAM,uBAAN,MAAM,CAAE,YAAY,KAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE;YACpE;;AAGF,QAAA,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;AAC1B,YAAA,YAAY,EAAE,IAAI;YAClB,aAAa;AACb,YAAA,UAAU,EAAE;AACb,SAAA,CAAC;AACJ,KAAC,EAAE,CAAC,aAAa,CAAC,CAAC;IAEnB,OAAO;QACL,WAAW;QACX;KACD;AACH;;;;"}
|
|
@@ -6,6 +6,10 @@ export interface ListScrollElementAttachRefs {
|
|
|
6
6
|
onScroll: ((e: {
|
|
7
7
|
scrollTop: number;
|
|
8
8
|
scrollLeft: number;
|
|
9
|
+
detail: {
|
|
10
|
+
scrollTop: number;
|
|
11
|
+
scrollLeft: number;
|
|
12
|
+
};
|
|
9
13
|
}) => void) | undefined;
|
|
10
14
|
onScrollToUpper: (() => void) | undefined;
|
|
11
15
|
onScrollToLower: (() => void) | undefined;
|
|
@@ -56,7 +56,9 @@ function useListScrollElementAttach(enabled, effectiveScrollElement, effectiveSt
|
|
|
56
56
|
const clientSize = isHorizontal ? target.clientWidth : target.clientHeight;
|
|
57
57
|
r.scrollCorrection.markUserScrolling();
|
|
58
58
|
updateRenderOffset(effectiveAdjusted, false, 'scrollElement');
|
|
59
|
-
|
|
59
|
+
const scrollTop = isHorizontal ? 0 : scrollPos;
|
|
60
|
+
const scrollLeft = isHorizontal ? scrollPos : 0;
|
|
61
|
+
(_a = r.onScroll) === null || _a === void 0 ? void 0 : _a.call(r, { scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } });
|
|
60
62
|
const { upper, lower } = r.threshold;
|
|
61
63
|
const innerContentLen = r.listContentLength;
|
|
62
64
|
const nowInUpper = effectiveAdjusted <= upper;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListScrollElementAttach.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttach.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\nimport type { RefObject } from 'react'\n\nexport interface ListScrollElementAttachRefs {\n scrollCorrection: { markUserScrolling: () => void }\n onScroll: ((e: { scrollTop: number, scrollLeft: number }) => void) | undefined\n onScrollToUpper: (() => void) | undefined\n onScrollToLower: (() => void) | undefined\n threshold: { upper: number, lower: number }\n listContentLength: number\n}\n\n/**\n * scrollElement 模式下:监听外部滚动驱动 renderOffset,并从 scrollElement 获取 containerLength\n */\nexport function useListScrollElementAttach(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\n effectiveStartOffset: number,\n isHorizontal: boolean,\n setContainerLength: (v: number) => void,\n updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void,\n scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined,\n refsRef: RefObject<ListScrollElementAttachRefs>\n) {\n const effectiveStartOffsetRef = useRef(effectiveStartOffset)\n effectiveStartOffsetRef.current = effectiveStartOffset\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current\n if (!el || typeof ResizeObserver === 'undefined') return\n\n const update = () => {\n const measured = isHorizontal ? el.clientWidth : el.clientHeight\n if (measured > 0) setContainerLength(measured)\n if (scrollRefProp) scrollRefProp.current = el\n }\n update()\n const ro = new ResizeObserver(update)\n ro.observe(el)\n return () => ro.disconnect()\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength])\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n\n let cancelled = false\n let teardown: (() => void) | null = null\n const maxRetries = 20\n\n const tryAttach = (retryCount = 0) => {\n if (cancelled) return\n const target = effectiveScrollElement.current\n if (!target) {\n if (retryCount < maxRetries) {\n requestAnimationFrame(() => tryAttach(retryCount + 1))\n }\n return\n }\n\n const refs = refsRef.current\n if (!refs) return\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = () => {\n const r = refsRef.current\n if (!r) return\n const scrollPos = isHorizontal ? target.scrollLeft : target.scrollTop\n const adjustedPos = scrollPos - effectiveStartOffsetRef.current\n const effectiveAdjusted = Math.max(0, adjustedPos)\n const clientSize = isHorizontal ? target.clientWidth : target.clientHeight\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n
|
|
1
|
+
{"version":3,"file":"useListScrollElementAttach.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttach.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\nimport type { RefObject } from 'react'\n\nexport interface ListScrollElementAttachRefs {\n scrollCorrection: { markUserScrolling: () => void }\n onScroll: ((e: { scrollTop: number, scrollLeft: number, detail: { scrollTop: number, scrollLeft: number } }) => void) | undefined\n onScrollToUpper: (() => void) | undefined\n onScrollToLower: (() => void) | undefined\n threshold: { upper: number, lower: number }\n listContentLength: number\n}\n\n/**\n * scrollElement 模式下:监听外部滚动驱动 renderOffset,并从 scrollElement 获取 containerLength\n */\nexport function useListScrollElementAttach(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\n effectiveStartOffset: number,\n isHorizontal: boolean,\n setContainerLength: (v: number) => void,\n updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void,\n scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined,\n refsRef: RefObject<ListScrollElementAttachRefs>\n) {\n const effectiveStartOffsetRef = useRef(effectiveStartOffset)\n effectiveStartOffsetRef.current = effectiveStartOffset\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current\n if (!el || typeof ResizeObserver === 'undefined') return\n\n const update = () => {\n const measured = isHorizontal ? el.clientWidth : el.clientHeight\n if (measured > 0) setContainerLength(measured)\n if (scrollRefProp) scrollRefProp.current = el\n }\n update()\n const ro = new ResizeObserver(update)\n ro.observe(el)\n return () => ro.disconnect()\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength])\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n\n let cancelled = false\n let teardown: (() => void) | null = null\n const maxRetries = 20\n\n const tryAttach = (retryCount = 0) => {\n if (cancelled) return\n const target = effectiveScrollElement.current\n if (!target) {\n if (retryCount < maxRetries) {\n requestAnimationFrame(() => tryAttach(retryCount + 1))\n }\n return\n }\n\n const refs = refsRef.current\n if (!refs) return\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = () => {\n const r = refsRef.current\n if (!r) return\n const scrollPos = isHorizontal ? target.scrollLeft : target.scrollTop\n const adjustedPos = scrollPos - effectiveStartOffsetRef.current\n const effectiveAdjusted = Math.max(0, adjustedPos)\n const clientSize = isHorizontal ? target.clientWidth : target.clientHeight\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n const scrollTop = isHorizontal ? 0 : scrollPos\n const scrollLeft = isHorizontal ? scrollPos : 0\n r.onScroll?.({ scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } })\n\n const { upper, lower } = r.threshold\n const innerContentLen = r.listContentLength\n const nowInUpper = effectiveAdjusted <= upper\n const nowInLower = innerContentLen > 0 && effectiveAdjusted + clientSize >= innerContentLen - lower\n if (nowInUpper && !inUpperZone) r.onScrollToUpper?.()\n if (nowInLower && !inLowerZone) r.onScrollToLower?.()\n inUpperZone = nowInUpper\n inLowerZone = nowInLower\n }\n\n const initialScroll = isHorizontal ? target.scrollLeft : target.scrollTop\n const initialAdjusted = Math.max(0, initialScroll - effectiveStartOffsetRef.current)\n updateRenderOffset(initialAdjusted, false, 'scrollElement')\n\n target.addEventListener('scroll', handler, { passive: true })\n teardown = () => target.removeEventListener('scroll', handler)\n }\n\n tryAttach()\n return () => {\n cancelled = true\n teardown?.()\n }\n }, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffset, updateRenderOffset, refsRef])\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -100,10 +100,9 @@ fallbackContainerLength) {
|
|
|
100
100
|
const effectiveAdjusted = Math.max(0, adjustedPos);
|
|
101
101
|
r.scrollCorrection.markUserScrolling();
|
|
102
102
|
updateRenderOffset(effectiveAdjusted, false, 'scrollElement');
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
});
|
|
103
|
+
const scrollTop = isHorizontal ? 0 : scrollPos;
|
|
104
|
+
const scrollLeft = isHorizontal ? scrollPos : 0;
|
|
105
|
+
(_l = r.onScroll) === null || _l === void 0 ? void 0 : _l.call(r, { scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } });
|
|
107
106
|
// 兜底:measure 未完成时 containerLengthRef 为 0,用 fallback 参与触顶/触底判断,避免漏触发
|
|
108
107
|
const clientSize = containerLengthRef.current || fallbackContainerLength || 0;
|
|
109
108
|
if (clientSize > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListScrollElementAttachWeapp.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttachWeapp.ts"],"sourcesContent":["import Taro from '@tarojs/taro'\nimport { useEffect, useRef } from 'react'\n\nimport type { MutableRefObject, RefObject } from 'react'\nimport type { ListScrollElementAttachRefs } from './useListScrollElementAttach'\n\n/**\n * 小程序版 scrollElement 模式:监听外部 scroll-view 的滚动事件驱动 renderOffset,\n * 并通过 createSelectorQuery 测量容器尺寸。\n *\n * 与 H5 版 useListScrollElementAttach 的区别:\n * - 容器尺寸通过 createSelectorQuery 测量(无 ResizeObserver)\n * - 滚动事件通过 TaroElement.addEventListener,格式为 TaroEvent(detail 合并到 target 上)\n * - 若 scroll-view 无 id,自动分配临时 id 供 SelectorQuery 使用\n */\nexport function useListScrollElementAttachWeapp(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\n effectiveStartOffsetRef: MutableRefObject<number>,\n effectiveStartOffset: number,\n isHorizontal: boolean,\n setContainerLength: (v: number) => void,\n updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void,\n scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined,\n refsRef: RefObject<ListScrollElementAttachRefs>,\n /**\n * 【兜底】容器尺寸测量失败或未完成时的回退值。\n *\n * 使用场景:\n * 1. 首次 scroll:SelectorQuery.exec 异步,scroll 事件可能早于测量回调,containerLengthRef 仍为 0\n * 2. 测量长期失败:SelectorQuery 返回空或 rect 尺寸为 0(如 id 错误、节点未挂载)\n *\n * 兜底逻辑:\n * - measure 回调:rect 为空或 measured≤0 时,若有 fallback 则写入 containerLengthRef 与 setContainerLength\n * - scroll handler:containerLengthRef 为 0 时,用 fallback 参与 onScrollToUpper/Lower 判断\n *\n * 取值建议:与 List 的 initialContainerLength 一致(默认 400,或 height/width prop),避免与 renderRange 等逻辑冲突。\n * 注意:fallback 与真实视口差异大时,onScrollToUpper/Lower 可能误触发;measure 每 150ms 轮询,正常会很快覆盖。\n */\n fallbackContainerLength?: number\n) {\n const containerLengthRef = useRef(0)\n const autoIdRef = useRef(`_ls_${Math.random().toString(36).slice(2, 9)}`)\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current as any\n if (!el) return\n if (scrollRefProp) scrollRefProp.current = el\n\n if (!el.id) {\n el.id = autoIdRef.current\n }\n const scrollViewId = el.id\n\n const measure = () => {\n const instance = Taro.getCurrentInstance()\n const query = instance?.page\n ? Taro.createSelectorQuery().in(instance.page as any)\n : Taro.createSelectorQuery()\n query\n .select(`#${scrollViewId}`)\n .boundingClientRect()\n .exec((res) => {\n const rect = res?.[0]\n if (rect) {\n const measured = isHorizontal ? rect.width : rect.height\n if (measured > 0) {\n containerLengthRef.current = measured\n setContainerLength(measured)\n return\n }\n }\n // 兜底:rect 为空或 measured≤0 时,使用 fallback 避免 containerLengthRef 长期为 0\n if (fallbackContainerLength != null && fallbackContainerLength > 0) {\n containerLengthRef.current = fallbackContainerLength\n setContainerLength(fallbackContainerLength)\n }\n })\n }\n\n measure()\n const interval = setInterval(measure, 150)\n return () => clearInterval(interval)\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength, fallbackContainerLength])\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n\n let cancelled = false\n let teardown: (() => void) | null = null\n const maxRetries = 20\n\n const tryAttach = (retryCount = 0) => {\n if (cancelled) return\n const target = effectiveScrollElement.current as any\n if (!target) {\n if (retryCount < maxRetries) {\n setTimeout(() => tryAttach(retryCount + 1), 50)\n }\n return\n }\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = (e: any) => {\n const r = refsRef.current\n if (!r) return\n\n const scrollPos = isHorizontal\n ? (e?.target?.scrollLeft ?? e?.mpEvent?.detail?.scrollLeft ?? 0)\n : (e?.target?.scrollTop ?? e?.mpEvent?.detail?.scrollTop ?? 0)\n const startOffset = effectiveStartOffsetRef.current\n const adjustedPos = scrollPos - startOffset\n const effectiveAdjusted = Math.max(0, adjustedPos)\n\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n r.onScroll?.({\n scrollTop: isHorizontal ? 0 : scrollPos,\n scrollLeft: isHorizontal ? scrollPos : 0,\n })\n\n // 兜底:measure 未完成时 containerLengthRef 为 0,用 fallback 参与触顶/触底判断,避免漏触发\n const clientSize = containerLengthRef.current || fallbackContainerLength || 0\n if (clientSize > 0) {\n const { upper, lower } = r.threshold\n const innerContentLen = r.listContentLength\n const nowInUpper = effectiveAdjusted <= upper\n const nowInLower = innerContentLen > 0 && effectiveAdjusted + clientSize >= innerContentLen - lower\n if (nowInUpper && !inUpperZone) r.onScrollToUpper?.()\n if (nowInLower && !inLowerZone) r.onScrollToLower?.()\n inUpperZone = nowInUpper\n inLowerZone = nowInLower\n }\n }\n\n target.addEventListener('scroll', handler)\n teardown = () => target.removeEventListener('scroll', handler)\n\n // 初始 renderOffset:weapp 无法直接读 target.scrollTop,需通过 SelectorQuery 查询\n const scrollViewId = target.id || autoIdRef.current\n if (!target.id) target.id = scrollViewId\n const instance = Taro.getCurrentInstance()\n const query = instance?.page\n ? Taro.createSelectorQuery().in(instance.page as any)\n : Taro.createSelectorQuery()\n query.select(`#${scrollViewId}`).scrollOffset().exec((res) => {\n const info = res?.[0]\n if (info) {\n const scrollTopVal = info.scrollTop ?? 0\n const scrollLeftVal = info.scrollLeft ?? 0\n const scrollPos = isHorizontal ? scrollLeftVal : scrollTopVal\n const startOffset = effectiveStartOffsetRef.current\n const initialAdjusted = Math.max(0, scrollPos - startOffset)\n updateRenderOffset(initialAdjusted, false, 'scrollElement')\n }\n })\n }\n\n tryAttach()\n return () => {\n cancelled = true\n teardown?.()\n }\n }, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffsetRef, effectiveStartOffset, updateRenderOffset, refsRef, fallbackContainerLength])\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useListScrollElementAttachWeapp.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttachWeapp.ts"],"sourcesContent":["import Taro from '@tarojs/taro'\nimport { useEffect, useRef } from 'react'\n\nimport type { MutableRefObject, RefObject } from 'react'\nimport type { ListScrollElementAttachRefs } from './useListScrollElementAttach'\n\n/**\n * 小程序版 scrollElement 模式:监听外部 scroll-view 的滚动事件驱动 renderOffset,\n * 并通过 createSelectorQuery 测量容器尺寸。\n *\n * 与 H5 版 useListScrollElementAttach 的区别:\n * - 容器尺寸通过 createSelectorQuery 测量(无 ResizeObserver)\n * - 滚动事件通过 TaroElement.addEventListener,格式为 TaroEvent(detail 合并到 target 上)\n * - 若 scroll-view 无 id,自动分配临时 id 供 SelectorQuery 使用\n */\nexport function useListScrollElementAttachWeapp(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\n effectiveStartOffsetRef: MutableRefObject<number>,\n effectiveStartOffset: number,\n isHorizontal: boolean,\n setContainerLength: (v: number) => void,\n updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void,\n scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined,\n refsRef: RefObject<ListScrollElementAttachRefs>,\n /**\n * 【兜底】容器尺寸测量失败或未完成时的回退值。\n *\n * 使用场景:\n * 1. 首次 scroll:SelectorQuery.exec 异步,scroll 事件可能早于测量回调,containerLengthRef 仍为 0\n * 2. 测量长期失败:SelectorQuery 返回空或 rect 尺寸为 0(如 id 错误、节点未挂载)\n *\n * 兜底逻辑:\n * - measure 回调:rect 为空或 measured≤0 时,若有 fallback 则写入 containerLengthRef 与 setContainerLength\n * - scroll handler:containerLengthRef 为 0 时,用 fallback 参与 onScrollToUpper/Lower 判断\n *\n * 取值建议:与 List 的 initialContainerLength 一致(默认 400,或 height/width prop),避免与 renderRange 等逻辑冲突。\n * 注意:fallback 与真实视口差异大时,onScrollToUpper/Lower 可能误触发;measure 每 150ms 轮询,正常会很快覆盖。\n */\n fallbackContainerLength?: number\n) {\n const containerLengthRef = useRef(0)\n const autoIdRef = useRef(`_ls_${Math.random().toString(36).slice(2, 9)}`)\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current as any\n if (!el) return\n if (scrollRefProp) scrollRefProp.current = el\n\n if (!el.id) {\n el.id = autoIdRef.current\n }\n const scrollViewId = el.id\n\n const measure = () => {\n const instance = Taro.getCurrentInstance()\n const query = instance?.page\n ? Taro.createSelectorQuery().in(instance.page as any)\n : Taro.createSelectorQuery()\n query\n .select(`#${scrollViewId}`)\n .boundingClientRect()\n .exec((res) => {\n const rect = res?.[0]\n if (rect) {\n const measured = isHorizontal ? rect.width : rect.height\n if (measured > 0) {\n containerLengthRef.current = measured\n setContainerLength(measured)\n return\n }\n }\n // 兜底:rect 为空或 measured≤0 时,使用 fallback 避免 containerLengthRef 长期为 0\n if (fallbackContainerLength != null && fallbackContainerLength > 0) {\n containerLengthRef.current = fallbackContainerLength\n setContainerLength(fallbackContainerLength)\n }\n })\n }\n\n measure()\n const interval = setInterval(measure, 150)\n return () => clearInterval(interval)\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength, fallbackContainerLength])\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n\n let cancelled = false\n let teardown: (() => void) | null = null\n const maxRetries = 20\n\n const tryAttach = (retryCount = 0) => {\n if (cancelled) return\n const target = effectiveScrollElement.current as any\n if (!target) {\n if (retryCount < maxRetries) {\n setTimeout(() => tryAttach(retryCount + 1), 50)\n }\n return\n }\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = (e: any) => {\n const r = refsRef.current\n if (!r) return\n\n const scrollPos = isHorizontal\n ? (e?.target?.scrollLeft ?? e?.mpEvent?.detail?.scrollLeft ?? 0)\n : (e?.target?.scrollTop ?? e?.mpEvent?.detail?.scrollTop ?? 0)\n const startOffset = effectiveStartOffsetRef.current\n const adjustedPos = scrollPos - startOffset\n const effectiveAdjusted = Math.max(0, adjustedPos)\n\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n const scrollTop = isHorizontal ? 0 : scrollPos\n const scrollLeft = isHorizontal ? scrollPos : 0\n r.onScroll?.({ scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } })\n\n // 兜底:measure 未完成时 containerLengthRef 为 0,用 fallback 参与触顶/触底判断,避免漏触发\n const clientSize = containerLengthRef.current || fallbackContainerLength || 0\n if (clientSize > 0) {\n const { upper, lower } = r.threshold\n const innerContentLen = r.listContentLength\n const nowInUpper = effectiveAdjusted <= upper\n const nowInLower = innerContentLen > 0 && effectiveAdjusted + clientSize >= innerContentLen - lower\n if (nowInUpper && !inUpperZone) r.onScrollToUpper?.()\n if (nowInLower && !inLowerZone) r.onScrollToLower?.()\n inUpperZone = nowInUpper\n inLowerZone = nowInLower\n }\n }\n\n target.addEventListener('scroll', handler)\n teardown = () => target.removeEventListener('scroll', handler)\n\n // 初始 renderOffset:weapp 无法直接读 target.scrollTop,需通过 SelectorQuery 查询\n const scrollViewId = target.id || autoIdRef.current\n if (!target.id) target.id = scrollViewId\n const instance = Taro.getCurrentInstance()\n const query = instance?.page\n ? Taro.createSelectorQuery().in(instance.page as any)\n : Taro.createSelectorQuery()\n query.select(`#${scrollViewId}`).scrollOffset().exec((res) => {\n const info = res?.[0]\n if (info) {\n const scrollTopVal = info.scrollTop ?? 0\n const scrollLeftVal = info.scrollLeft ?? 0\n const scrollPos = isHorizontal ? scrollLeftVal : scrollTopVal\n const startOffset = effectiveStartOffsetRef.current\n const initialAdjusted = Math.max(0, scrollPos - startOffset)\n updateRenderOffset(initialAdjusted, false, 'scrollElement')\n }\n })\n }\n\n tryAttach()\n return () => {\n cancelled = true\n teardown?.()\n }\n }, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffsetRef, effectiveStartOffset, updateRenderOffset, refsRef, fallbackContainerLength])\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,10 @@ export interface ListProps {
|
|
|
12
12
|
onScroll?: (e: {
|
|
13
13
|
scrollTop: number;
|
|
14
14
|
scrollLeft: number;
|
|
15
|
+
detail: {
|
|
16
|
+
scrollTop: number;
|
|
17
|
+
scrollLeft: number;
|
|
18
|
+
};
|
|
15
19
|
}) => void;
|
|
16
20
|
onScrollToUpper?: () => void;
|
|
17
21
|
onScrollToLower?: () => void;
|
|
@@ -37,12 +41,9 @@ export interface ListProps {
|
|
|
37
41
|
width?: number | string;
|
|
38
42
|
style?: React.CSSProperties;
|
|
39
43
|
children?: React.ReactNode;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
itemHeight?: number;
|
|
43
|
-
itemWidth?: number;
|
|
44
|
+
/** Header 沿滚动方向尺寸;未传时回退到 itemSize */
|
|
45
|
+
headerSize?: number;
|
|
44
46
|
useResizeObserver?: boolean;
|
|
45
|
-
estimatedItemSize?: number;
|
|
46
47
|
onItemSizeChange?: (index: number, size: number) => void;
|
|
47
48
|
showNoMore?: boolean;
|
|
48
49
|
noMoreText?: string;
|
|
@@ -65,13 +65,31 @@ function resolveScrollTargetOffset(options, isHorizontal) {
|
|
|
65
65
|
}
|
|
66
66
|
// eslint-disable-next-line complexity -- List 多端/多模式逻辑集中,已抽离 useListNestedScroll、useListScrollElementAttach、resolveScrollTargetOffset 等
|
|
67
67
|
const InnerList = (props, ref) => {
|
|
68
|
-
var _a, _b
|
|
68
|
+
var _a, _b;
|
|
69
69
|
const { stickyHeader = false, space = 0, height = 400, width = '100%', showScrollbar = true, scrollTop: controlledScrollTop, scrollX = false, scrollY = true, onScroll, onScrollToUpper, onScrollToLower, onScrollStart, onScrollEnd, upperThreshold = 50, lowerThreshold = 50, cacheCount = 2, cacheExtent, enableBackToTop, className, style, children, nestedScroll, scrollElement, scrollRef: scrollRefProp, } = props;
|
|
70
70
|
const isHorizontal = scrollX === true;
|
|
71
71
|
const listType = nestedScroll === true ? 'nested' : 'default';
|
|
72
72
|
const { effectiveScrollElement, effectiveStartOffset, effectiveStartOffsetRef, useScrollElementMode, needAutoFind, autoFindStatus, contentWrapperRef, contentId, } = useListNestedScroll(listType, scrollElement, undefined, isHorizontal);
|
|
73
73
|
const DEFAULT_ITEM_WIDTH = 120;
|
|
74
74
|
const DEFAULT_ITEM_HEIGHT = 40;
|
|
75
|
+
const defaultItemSize = isHorizontal ? DEFAULT_ITEM_WIDTH : DEFAULT_ITEM_HEIGHT;
|
|
76
|
+
const normalizeSize = React.useCallback((value) => {
|
|
77
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
78
|
+
return null;
|
|
79
|
+
return value;
|
|
80
|
+
}, []);
|
|
81
|
+
const resolveItemSizeByIndex = React.useCallback((index, fallback) => {
|
|
82
|
+
const { itemSize, itemData } = props;
|
|
83
|
+
const numberSize = normalizeSize(itemSize);
|
|
84
|
+
if (numberSize != null)
|
|
85
|
+
return numberSize;
|
|
86
|
+
if (typeof itemSize === 'function') {
|
|
87
|
+
const functionSize = normalizeSize(itemSize(index, itemData));
|
|
88
|
+
if (functionSize != null)
|
|
89
|
+
return functionSize;
|
|
90
|
+
}
|
|
91
|
+
return fallback;
|
|
92
|
+
}, [props.itemSize, props.itemData, normalizeSize]);
|
|
75
93
|
// 滚动状态管理
|
|
76
94
|
const containerRef = React.useRef(null);
|
|
77
95
|
// 生成唯一 List ID(用于小程序 ResizeObserver)
|
|
@@ -326,8 +344,7 @@ const InnerList = (props, ref) => {
|
|
|
326
344
|
return result;
|
|
327
345
|
}, [children]);
|
|
328
346
|
// 动态尺寸管理
|
|
329
|
-
const
|
|
330
|
-
const estimatedSize = (_a = props.estimatedItemSize) !== null && _a !== void 0 ? _a : defaultEstimatedSize;
|
|
347
|
+
const estimatedSize = resolveItemSizeByIndex(0, defaultItemSize);
|
|
331
348
|
// 计算总 item 数量(跨所有 section)
|
|
332
349
|
const totalItemCount = React.useMemo(() => {
|
|
333
350
|
return sections.reduce((sum, section) => sum + section.items.length, 0);
|
|
@@ -346,7 +363,7 @@ const InnerList = (props, ref) => {
|
|
|
346
363
|
// 动态尺寸缓存
|
|
347
364
|
const sizeCache = useItemSizeCache({
|
|
348
365
|
isHorizontal,
|
|
349
|
-
|
|
366
|
+
estimatedSize,
|
|
350
367
|
itemCount: totalItemCount
|
|
351
368
|
});
|
|
352
369
|
// header 动态尺寸缓存(sectionIndex -> size)
|
|
@@ -375,6 +392,9 @@ const InnerList = (props, ref) => {
|
|
|
375
392
|
onScrollToLowerRef.current = onScrollToLower;
|
|
376
393
|
const thresholdRef = React.useRef({ upper: upperThreshold, lower: lowerThreshold });
|
|
377
394
|
thresholdRef.current = { upper: upperThreshold, lower: lowerThreshold };
|
|
395
|
+
const listContentLengthRef = React.useRef(0);
|
|
396
|
+
const inUpperZoneRef = React.useRef(true);
|
|
397
|
+
const inLowerZoneRef = React.useRef(false);
|
|
378
398
|
// 小程序 + 动高(virtual-list 风格):测量变化后同帧重排,不做程序性 scrollTop 回拉
|
|
379
399
|
const scheduleWeappDynamicReflow = React.useCallback(() => {
|
|
380
400
|
if (!isWeapp || props.useResizeObserver !== true)
|
|
@@ -440,30 +460,12 @@ const InnerList = (props, ref) => {
|
|
|
440
460
|
});
|
|
441
461
|
}
|
|
442
462
|
}, [sizeCache, scrollCorrection, estimatedSize, isWeapp, props.useResizeObserver, scheduleWeappDynamicReflow]);
|
|
443
|
-
const getDefaultHeaderSize = () => {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if (typeof props.itemSize === 'number')
|
|
450
|
-
return props.itemSize;
|
|
451
|
-
if (typeof props.itemSize === 'function')
|
|
452
|
-
return props.itemSize(0, props.itemData) || DEFAULT_ITEM_WIDTH;
|
|
453
|
-
return DEFAULT_ITEM_WIDTH;
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
if (typeof props.headerHeight === 'number')
|
|
457
|
-
return props.headerHeight;
|
|
458
|
-
if (typeof props.itemHeight === 'number')
|
|
459
|
-
return props.itemHeight;
|
|
460
|
-
if (typeof props.itemSize === 'number')
|
|
461
|
-
return props.itemSize;
|
|
462
|
-
if (typeof props.itemSize === 'function')
|
|
463
|
-
return props.itemSize(0, props.itemData) || DEFAULT_ITEM_HEIGHT;
|
|
464
|
-
return DEFAULT_ITEM_HEIGHT;
|
|
465
|
-
}
|
|
466
|
-
};
|
|
463
|
+
const getDefaultHeaderSize = React.useCallback(() => {
|
|
464
|
+
const headerSize = normalizeSize(props.headerSize);
|
|
465
|
+
if (headerSize != null)
|
|
466
|
+
return headerSize;
|
|
467
|
+
return resolveItemSizeByIndex(0, defaultItemSize);
|
|
468
|
+
}, [props.headerSize, resolveItemSizeByIndex, defaultItemSize, normalizeSize]);
|
|
467
469
|
// 获取 header 尺寸(支持动态测量)
|
|
468
470
|
const getHeaderSize = React.useCallback((sectionIndex) => {
|
|
469
471
|
if (props.useResizeObserver === true) {
|
|
@@ -472,30 +474,13 @@ const InnerList = (props, ref) => {
|
|
|
472
474
|
return cached;
|
|
473
475
|
}
|
|
474
476
|
return getDefaultHeaderSize();
|
|
475
|
-
}, [props.useResizeObserver,
|
|
477
|
+
}, [props.useResizeObserver, getDefaultHeaderSize]);
|
|
476
478
|
const getItemSize = React.useCallback((index) => {
|
|
477
479
|
if (props.useResizeObserver === true) {
|
|
478
480
|
return sizeCache.getItemSize(index);
|
|
479
481
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return props.itemWidth;
|
|
483
|
-
if (typeof props.itemSize === 'number')
|
|
484
|
-
return props.itemSize;
|
|
485
|
-
if (typeof props.itemSize === 'function')
|
|
486
|
-
return props.itemSize(index, props.itemData) || DEFAULT_ITEM_WIDTH;
|
|
487
|
-
return DEFAULT_ITEM_WIDTH;
|
|
488
|
-
}
|
|
489
|
-
else {
|
|
490
|
-
if (typeof props.itemHeight === 'number')
|
|
491
|
-
return props.itemHeight;
|
|
492
|
-
if (typeof props.itemSize === 'number')
|
|
493
|
-
return props.itemSize;
|
|
494
|
-
if (typeof props.itemSize === 'function')
|
|
495
|
-
return props.itemSize(index, props.itemData) || DEFAULT_ITEM_HEIGHT;
|
|
496
|
-
return DEFAULT_ITEM_HEIGHT;
|
|
497
|
-
}
|
|
498
|
-
}, [props.useResizeObserver, props.itemWidth, props.itemHeight, props.itemSize, props.itemData, isHorizontal, sizeCache]);
|
|
482
|
+
return resolveItemSizeByIndex(index, defaultItemSize);
|
|
483
|
+
}, [props.useResizeObserver, sizeCache, resolveItemSizeByIndex, defaultItemSize]);
|
|
499
484
|
// 分组累积高度/宽度,sizeCacheVersion 变化时重算
|
|
500
485
|
const sectionOffsets = React.useMemo(() => {
|
|
501
486
|
const offsets = [0];
|
|
@@ -510,7 +495,7 @@ const InnerList = (props, ref) => {
|
|
|
510
495
|
globalItemIndex += section.items.length;
|
|
511
496
|
});
|
|
512
497
|
return offsets;
|
|
513
|
-
}, [sections, space,
|
|
498
|
+
}, [sections, space, getItemSize, getHeaderSize, sizeCacheVersion]);
|
|
514
499
|
// 外层虚拟滚动:可见分组
|
|
515
500
|
const [startSection, endSection] = React.useMemo(() => {
|
|
516
501
|
let start = 0;
|
|
@@ -570,6 +555,7 @@ const InnerList = (props, ref) => {
|
|
|
570
555
|
}
|
|
571
556
|
}, [visibleStartItem, visibleEndItem, props.onScrollIndex]);
|
|
572
557
|
const handleScroll = React.useCallback((e) => {
|
|
558
|
+
var _a, _b, _c, _d;
|
|
573
559
|
let newOffset;
|
|
574
560
|
if (e.detail) {
|
|
575
561
|
newOffset = isHorizontal ? e.detail.scrollLeft : e.detail.scrollTop;
|
|
@@ -578,6 +564,14 @@ const InnerList = (props, ref) => {
|
|
|
578
564
|
newOffset = isHorizontal ? e.scrollLeft : e.scrollTop;
|
|
579
565
|
}
|
|
580
566
|
const effectiveOffset = newOffset;
|
|
567
|
+
const currentThreshold = thresholdRef.current;
|
|
568
|
+
const { upper, lower } = currentThreshold;
|
|
569
|
+
const currentContainerLength = containerRef.current
|
|
570
|
+
? (isHorizontal ? containerRef.current.clientWidth : containerRef.current.clientHeight)
|
|
571
|
+
: containerLength;
|
|
572
|
+
const nowInUpper = effectiveOffset <= upper;
|
|
573
|
+
const currentContentLength = listContentLengthRef.current;
|
|
574
|
+
const nowInLower = currentContentLength > 0 && effectiveOffset + currentContainerLength >= currentContentLength - lower;
|
|
581
575
|
const diff = effectiveOffset - lastScrollTopRef.current;
|
|
582
576
|
scrollDiffListRef.current.shift();
|
|
583
577
|
scrollDiffListRef.current.push(diff);
|
|
@@ -587,19 +581,29 @@ const InnerList = (props, ref) => {
|
|
|
587
581
|
if (programmaticCooldownRef.current) {
|
|
588
582
|
lastScrollTopRef.current = effectiveOffset;
|
|
589
583
|
setRenderOffset(effectiveOffset);
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
584
|
+
const scrollTop = isHorizontal ? 0 : newOffset;
|
|
585
|
+
const scrollLeft = isHorizontal ? newOffset : 0;
|
|
586
|
+
onScroll === null || onScroll === void 0 ? void 0 : onScroll({ scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } });
|
|
587
|
+
if (nowInUpper && !inUpperZoneRef.current)
|
|
588
|
+
(_a = onScrollToUpperRef.current) === null || _a === void 0 ? void 0 : _a.call(onScrollToUpperRef);
|
|
589
|
+
if (nowInLower && !inLowerZoneRef.current)
|
|
590
|
+
(_b = onScrollToLowerRef.current) === null || _b === void 0 ? void 0 : _b.call(onScrollToLowerRef);
|
|
591
|
+
inUpperZoneRef.current = nowInUpper;
|
|
592
|
+
inLowerZoneRef.current = nowInLower;
|
|
594
593
|
return;
|
|
595
594
|
}
|
|
596
595
|
scrollCorrection.markUserScrolling();
|
|
597
596
|
updateRenderOffset(effectiveOffset, false, 'onScroll');
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
597
|
+
const scrollTop = isHorizontal ? 0 : newOffset;
|
|
598
|
+
const scrollLeft = isHorizontal ? newOffset : 0;
|
|
599
|
+
onScroll === null || onScroll === void 0 ? void 0 : onScroll({ scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } });
|
|
600
|
+
if (nowInUpper && !inUpperZoneRef.current)
|
|
601
|
+
(_c = onScrollToUpperRef.current) === null || _c === void 0 ? void 0 : _c.call(onScrollToUpperRef);
|
|
602
|
+
if (nowInLower && !inLowerZoneRef.current)
|
|
603
|
+
(_d = onScrollToLowerRef.current) === null || _d === void 0 ? void 0 : _d.call(onScrollToLowerRef);
|
|
604
|
+
inUpperZoneRef.current = nowInUpper;
|
|
605
|
+
inLowerZoneRef.current = nowInLower;
|
|
606
|
+
}, [isHorizontal, onScroll, updateRenderOffset, scrollCorrection, props.useResizeObserver, containerLength]);
|
|
603
607
|
// 小程序:onScrollEnd 优先结束 isUserScrolling,timeout 兜底
|
|
604
608
|
const handleNativeScrollEnd = React.useCallback(() => {
|
|
605
609
|
if (isWeapp) {
|
|
@@ -744,9 +748,7 @@ const InnerList = (props, ref) => {
|
|
|
744
748
|
// ScrollView 属性
|
|
745
749
|
const scrollViewProps = Object.assign(Object.assign(Object.assign(Object.assign({ scrollY: !scrollX && scrollY, scrollX, style: containerStyle, className, enhanced: true, showScrollbar,
|
|
746
750
|
upperThreshold,
|
|
747
|
-
lowerThreshold, scrollWithAnimation: false, onScroll: handleScroll,
|
|
748
|
-
onScrollToLower,
|
|
749
|
-
onScrollStart, onScrollEnd: handleNativeScrollEnd, enableBackToTop }, (isWeapp ? { scrollAnchoring: true } : {})), (typeof cacheExtent === 'number' ? { cacheExtent } : {})), scrollViewRefresherProps), scrollViewRefresherHandlers);
|
|
751
|
+
lowerThreshold, scrollWithAnimation: false, onScroll: handleScroll, onScrollStart, onScrollEnd: handleNativeScrollEnd, enableBackToTop }, (isWeapp ? { scrollAnchoring: true } : {})), (typeof cacheExtent === 'number' ? { cacheExtent } : {})), scrollViewRefresherProps), scrollViewRefresherHandlers);
|
|
750
752
|
// H5 对齐小程序:refresherTriggered=true 时顶部立即显示加载指示器,滚到顶部并锁定滚动直至设为 false
|
|
751
753
|
React.useEffect(() => {
|
|
752
754
|
if (!isH5 || !refresherConfig || supportsNativeRefresher || !h5RefresherProps.isRefreshing)
|
|
@@ -835,7 +837,6 @@ const InnerList = (props, ref) => {
|
|
|
835
837
|
const listContentLength = sectionOffsets[sectionOffsets.length - 1] + noMoreHeight;
|
|
836
838
|
const totalLength = listContentLength;
|
|
837
839
|
// scrollElement 模式下 onScrollToLower 需用内层内容高度判断,供 scroll handler 读取
|
|
838
|
-
const listContentLengthRef = React.useRef(0);
|
|
839
840
|
listContentLengthRef.current = listContentLength;
|
|
840
841
|
const scrollAttachRefsRef = React.useRef(null);
|
|
841
842
|
scrollAttachRefsRef.current = {
|
|
@@ -874,7 +875,7 @@ const InnerList = (props, ref) => {
|
|
|
874
875
|
}
|
|
875
876
|
}
|
|
876
877
|
return null;
|
|
877
|
-
}, [stickyHeader, renderOffset, sectionOffsets, sections
|
|
878
|
+
}, [stickyHeader, renderOffset, sectionOffsets, sections]);
|
|
878
879
|
// 渲染分组+item双层虚拟滚动
|
|
879
880
|
const renderSections = () => {
|
|
880
881
|
const nodes = [];
|
|
@@ -1218,7 +1219,7 @@ const InnerList = (props, ref) => {
|
|
|
1218
1219
|
height: refresherHeightForH5,
|
|
1219
1220
|
zIndex: 0,
|
|
1220
1221
|
transform: `translateY(${h5RefresherTranslateY}px)`,
|
|
1221
|
-
}, children: renderRefresherContent() }), jsxs(View, { style: Object.assign(Object.assign(Object.assign({}, listWrapperStyle), pullTranslate), { zIndex: 1, background: (
|
|
1222
|
+
}, children: renderRefresherContent() }), jsxs(View, { style: Object.assign(Object.assign(Object.assign({}, listWrapperStyle), pullTranslate), { zIndex: 1, background: (_b = (_a = style === null || style === void 0 ? void 0 : style.background) !== null && _a !== void 0 ? _a : style === null || style === void 0 ? void 0 : style.backgroundColor) !== null && _b !== void 0 ? _b : '#fff' }), children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })] })) : (jsxs(Fragment, { children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })) }));
|
|
1222
1223
|
}
|
|
1223
1224
|
return (jsxs(ScrollView, Object.assign({ ref: containerRef }, scrollViewProps, { id: listId, children: [supportsNativeRefresher && renderRefresherContent(), renderContentArea()] })));
|
|
1224
1225
|
};
|