yh-react-virtuallist
Version:
yh-react-virtuallist 虚拟列表组件
141 lines (140 loc) • 5.54 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import React, { useEffect, useMemo, useRef, useState, } from "react";
import { unstable_batchedUpdates } from "react-dom";
import SuperVirtualList from "./superVirtualList";
export { SuperVirtualList };
function arrayResolve(value, isArrayFunc, notArrayFunc) {
if (Array.isArray(value)) {
return isArrayFunc(value);
}
else {
console.error("you must pass array children ");
return notArrayFunc();
}
}
export function VirtualList(props) {
const { children, scrollDom, itemHeight, renderNumber } = props, rest = __rest(props, ["children", "scrollDom", "itemHeight", "renderNumber"]);
const [scrollDomParams, setScrollDomParams] = useState({
width: 0,
height: 0,
top: 0,
left: 0,
});
useEffect(() => {
if (props.scrollDom.current) {
const rect = props.scrollDom.current.getBoundingClientRect();
setScrollDomParams({
width: rect.width,
height: rect.height,
left: rect.left,
top: rect.top,
});
}
}, [props.scrollDom]);
const [childrenWrapParams, setChildrenWrapParams] = useState({
width: 0,
height: 0,
top: 0,
left: 0,
});
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
const rect = ref.current.getBoundingClientRect();
setChildrenWrapParams({
width: rect.width,
height: rect.height,
left: rect.left,
top: rect.top,
});
}
}, []);
const wrapperToScrollDomDistance = useMemo(() => {
return childrenWrapParams.top - scrollDomParams.top;
}, [childrenWrapParams.top, scrollDomParams.top]);
const mockHeight = useMemo(() => {
return arrayResolve(props.children, (val) => val.length * props.itemHeight -
wrapperToScrollDomDistance -
scrollDomParams.height, () => 0);
}, [
props.children,
props.itemHeight,
scrollDomParams.height,
wrapperToScrollDomDistance,
]);
const [renderChildren, setRenderChildren] = useState(
//一开始,需要返回对应截取的元素
() => {
return arrayResolve(props.children, (val) => val.slice(0, props.renderNumber), () => null);
});
const [viewPortY, setViewPortY] = useState(0);
useEffect(() => {
let fn;
if (props.scrollDom.current) {
fn = (e) => {
const target = e.target;
const scroll = target.scrollTop - scrollDomParams.height;
const lenth = arrayResolve(props.children, (val) => val.length, () => 0);
let sindex = Math.floor(scroll / props.itemHeight);
if (sindex < 0) {
sindex = 0;
}
const remain = props.renderNumber + sindex + props.renderNumber > lenth
? lenth
: props.renderNumber + props.renderNumber + sindex;
let Y = scroll -
wrapperToScrollDomDistance -
scrollDomParams.height;
if (Y < 0) {
Y = 0;
}
else if (Y >=
mockHeight - scrollDomParams.height - scrollDomParams.height) {
Y =
mockHeight -
scrollDomParams.height -
scrollDomParams.height;
}
unstable_batchedUpdates(() => {
setRenderChildren(arrayResolve(props.children, (val) => val.slice(0 + sindex, remain), () => null));
setViewPortY(Y);
});
};
props.scrollDom.current.addEventListener("scroll", fn);
}
return () => {
if (props.scrollDom.current) {
//解绑非常重要,否则渲再次出现渲染会出严重问题
props.scrollDom.current.removeEventListener("scroll", fn);
}
};
}, [
mockHeight,
props.children,
props.itemHeight,
props.renderNumber,
props.scrollDom,
scrollDomParams.height,
wrapperToScrollDomDistance,
]);
return (React.createElement("div", Object.assign({ className: "yh-virtuallist", style: { display: "flex", position: "relative", width: "100%" } }, rest),
React.createElement("div", { style: { height: mockHeight } }),
React.createElement("div", { ref: ref, style: {
position: "absolute",
transform: `translate3d(0px, ${viewPortY}px, 0px)`,
width: "100%",
} },
renderChildren,
React.createElement("div", { style: { height: "1px" } }))));
}
export default VirtualList;