UNPKG

yh-react-virtuallist

Version:

yh-react-virtuallist 虚拟列表组件

141 lines (140 loc) 5.54 kB
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;