@nutui/nutui-react-taro
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
173 lines (172 loc) • 6.55 kB
JavaScript
import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
import { _ as _object_spread_props } from "@swc/helpers/_/_object_spread_props";
import { _ as _object_without_properties } from "@swc/helpers/_/_object_without_properties";
import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ScrollView, View } from "@tarojs/components";
import classNames from "classnames";
import { getWindowInfo } from "../../utils/taro/get-system-info";
import { initPositinoCache, updateItemSize } from "./utils";
import { ComponentDefaults } from "../../utils/typings";
var defaultProps = _object_spread_props(_object_spread({}, ComponentDefaults), {
list: [],
itemHeight: 66,
margin: 10,
itemEqual: true,
overscan: 2
});
export var VirtualList = function(props) {
var _ref = _object_spread({}, defaultProps, props), list = _ref.list, itemRender = _ref.itemRender, itemHeight = _ref.itemHeight, margin = _ref.margin, itemEqual = _ref.itemEqual, overscan = _ref.overscan, key = _ref.key, onScroll = _ref.onScroll, className = _ref.className, origContainerHeight = _ref.containerHeight, rest = _object_without_properties(_ref, [
"list",
"itemRender",
"itemHeight",
"margin",
"itemEqual",
"overscan",
"key",
"onScroll",
"className",
"containerHeight"
]);
var clientHeight = useMemo(function() {
return getWindowInfo().windowHeight - 5 || 667;
}, []);
var containerHeight = useMemo(function() {
return origContainerHeight !== null && origContainerHeight !== void 0 ? origContainerHeight : clientHeight;
}, [
origContainerHeight,
clientHeight
]);
var _useState = _sliced_to_array(useState(0), 2), startOffset = _useState[0], setStartOffset = _useState[1];
var start = useRef(0);
// 虚拟列表容器ref
var scrollRef = useRef(null);
// 虚拟列表显示区域ref
var itemsRef = useRef(null);
// 列表位置信息
var _useState1 = _sliced_to_array(useState([
{
index: 0,
left: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
right: 0
}
]), 2), positions = _useState1[0], setPositions = _useState1[1];
var _useState2 = _sliced_to_array(useState(containerHeight || 0), 2), offSetSize = _useState2[0], setOffSetSize = _useState2[1];
// 初始计算可视区域展示数量
useEffect(function() {
setPositions(function(options) {
return _object_spread_props(_object_spread({}, options), {
endIndex: visibleCount()
});
});
}, [
itemHeight,
overscan,
offSetSize
]);
useEffect(function() {
if (containerHeight) return;
setOffSetSize(getContainerHeight());
}, [
containerHeight
]);
useEffect(function() {
var pos = initPositinoCache(itemHeight + margin, list.length);
setPositions(pos);
}, [
itemHeight,
list
]);
// 可视区域总高度
var getContainerHeight = function() {
// 初始首页列表高度
var initH = (itemHeight + margin) * list.length;
// 未设置containerHeight高度,判断首页高度小于设备高度时,滚动容器高度为首页数据高度,减5为分页触发的偏移量
return initH < clientHeight ? initH + overscan * (itemHeight + margin) - 5 : Math.min(containerHeight, clientHeight) // Math.min(containerHeight, clientHeight)
;
};
// 可视区域条数
var visibleCount = function() {
return Math.ceil(getContainerHeight() / (itemHeight + margin)) + overscan;
};
var end = function() {
return start.current + visibleCount();
};
var listHeight = function() {
return list.length * (itemHeight + margin);
};
var visibleData = function() {
return list.slice(start.current, Math.min(end(), list.length));
};
var updateTotalSize = useCallback(function() {
if (!itemsRef.current) return;
var items = itemsRef.current.children;
if (!items.length) return;
// 更新缓存
updateItemSize(positions, items, 'height', margin);
}, [
positions
]);
// 滚动监听
var listScroll = function(e) {
var scrollTop = e.target.scrollTop;
if (scrollTop <= 0) {
e.target.scrollTop = 0;
return setStartOffset(0);
}
if (!itemEqual) {
updateTotalSize();
}
start.current = Math.floor(scrollTop / (itemHeight + margin));
setStartOffset(scrollTop - scrollTop % (itemHeight + margin));
var endIndex = end();
// list 变动说明触底
if (endIndex > list.length - 1) {
onScroll && onScroll();
}
};
return /*#__PURE__*/ React.createElement(View, _object_spread_props(_object_spread({
className: classNames('nut-virtualList-box', className)
}, rest), {
style: {
height: containerHeight ? "".concat(offSetSize, "px") : ''
}
}), /*#__PURE__*/ React.createElement(ScrollView, {
scrollY: true,
bounces: false,
type: "list",
ref: scrollRef,
className: "nut-virtuallist",
style: {
height: "".concat(getContainerHeight(), "px")
},
onScroll: listScroll
}, /*#__PURE__*/ React.createElement(View, {
className: "nut-virtuallist-phantom",
style: {
height: "".concat(listHeight(), "px")
}
}), /*#__PURE__*/ React.createElement(View, {
className: "nut-virtuallist-container",
ref: itemsRef,
style: {
transform: "translate3d(0, ".concat(startOffset, "px, 0)")
}
}, visibleData().map(function(data, index) {
var dataIndex = start.current + index;
var keyVal = key && data[key] ? data[key] : dataIndex;
return /*#__PURE__*/ React.createElement(View, {
"data-index": "".concat(dataIndex),
className: "nut-virtuallist-item",
key: "".concat(keyVal),
style: {
height: "".concat(itemEqual ? "".concat(itemHeight, "px") : 'auto')
}
}, itemRender ? itemRender(data, dataIndex, index) : data);
}))));
};
VirtualList.displayName = 'NutVirtualList';