@z-cloud/virtual-taro
Version:
一个基于React + TS开发的虚拟列表,支持瀑布流、grid的组件。
135 lines (134 loc) • 4.83 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { useVirualizer, useIsomorphicLayoutEffect } from "../hooks/use-virtualizer.js";
import { ScrollView, View } from "@tarojs/components";
import { virtualizerUUID, getWindowRect, getRectSizeAsync, getScrollViewContextNode } from "../utils/index.js";
import Taro, { usePageScroll } from "@tarojs/taro";
import { Resizable } from "./resizable.js";
import { useState } from "react";
function NormalVirtualList({
children,
className,
itemClassName,
style,
itemStyle,
dynamicSize = false,
scrollViewProps,
onReady,
...props
}) {
const [scrollId] = useState(() => `zcloud-virtual-list-${virtualizerUUID.value++}`);
const virtualizer = useVirualizer(props);
const { lanes = 1, horizontal } = props;
const totalSize = virtualizer.getTotalSize();
let gridTemplateAreas = Array.from(
{ length: lanes },
(_, index) => horizontal ? `"lane${index}"` : `lane${index}`
).join(" ");
if (!horizontal) {
gridTemplateAreas = `"${gridTemplateAreas}"`;
}
usePageScroll((e) => {
virtualizer.onScroll(e);
});
const onScroll = (e) => {
var _a;
virtualizer.onScroll(e.detail);
(_a = scrollViewProps == null ? void 0 : scrollViewProps.onScroll) == null ? void 0 : _a.call(scrollViewProps, e);
};
const init = async () => {
const windowRect = getWindowRect();
const elementRect = await getRectSizeAsync(scrollId);
const scrollNode = await getScrollViewContextNode(scrollId);
virtualizer.setScrollElementRect(props.followPageScroll ? windowRect : elementRect);
virtualizer.scrollTo = (offset, behavior) => {
if (!scrollNode) {
console.warn("获取scrollNode失败");
return;
}
scrollNode.scrollTo({
[props.horizontal ? "left" : "top"]: offset,
animated: dynamicSize ? false : behavior === "smooth"
});
};
if (props.followPageScroll) {
virtualizer.options.scrollMargin = elementRect.top ?? 0;
virtualizer.scrollTo = (offset, behavior) => {
Taro.pageScrollTo({
scrollTop: offset,
// 动态尺寸不支持滚动动画 小程序都不建议使用动画
duration: behavior === "smooth" && !dynamicSize ? 300 : 0
});
};
}
virtualizer.init();
onReady == null ? void 0 : onReady(virtualizer);
};
useIsomorphicLayoutEffect(() => {
init();
return () => virtualizer.clean();
}, [virtualizer]);
return /* @__PURE__ */ jsx(
ScrollView,
{
...scrollViewProps,
scrollX: horizontal,
scrollY: !horizontal,
enhanced: true,
className,
style: { overflow: "auto", ...style },
id: scrollId,
onScroll,
children: /* @__PURE__ */ jsx(
View,
{
style: {
display: "grid",
gridTemplateAreas,
gridTemplateColumns: `repeat(${horizontal ? 1 : lanes}, 1fr)`,
gridTemplateRows: `repeat(${horizontal ? lanes : 1}, 1fr)`,
alignItems: dynamicSize && !horizontal ? "start" : "stretch",
justifyItems: dynamicSize && horizontal ? "start" : "stretch",
columnGap: horizontal ? void 0 : props.gap,
rowGap: horizontal ? props.gap : void 0,
height: props.horizontal ? "100%" : totalSize,
width: props.horizontal ? totalSize : "100%"
},
children: virtualizer.getVirtualItems().map((virtualItem) => /* @__PURE__ */ jsxs(
View,
{
className: itemClassName,
"data-index": virtualItem.index,
style: {
...itemStyle,
gridArea: `lane${virtualItem.lane}`,
height: horizontal || dynamicSize ? void 0 : virtualItem.size,
width: !horizontal || dynamicSize ? void 0 : virtualItem.size,
transform: props.horizontal ? `translateX(${virtualItem.start}px)` : `translateY(${virtualItem.start - (props.followPageScroll ? virtualizer.options.scrollMargin ?? 0 : 0)}px)`
},
children: [
dynamicSize && /* @__PURE__ */ jsx(
Resizable,
{
emitWhenMounted: true,
listId: `${scrollId}-${virtualItem.index}`,
style: { width: "100%" },
onResize: (res) => virtualizer.onElementSizeChange(virtualItem.index, {
width: res.width,
height: res.height
}),
children: children(virtualItem)
}
),
!dynamicSize && children(virtualItem)
]
},
virtualItem.index
))
}
)
}
);
}
export {
NormalVirtualList
};