js实现前端无限滚动中的虚拟列表效果(只渲染可视区域,dom元素可复用)

先看效果

不加防抖节流。
在这里插入图片描述
加防抖节流
在这里插入图片描述

提示

加防抖节流 可以节省性能,但滑动过快会出现闪烁现象。请根据需求自行调整触发时间。原理通过代码可看出!

限制:该代码处理方式由于html 元素高度存在最大值,且不同浏览器以及不同浏览器版本支持的html 元素高度最大值不一样,当数据列表理论生成的高度大于html元素高度最大值时,生成的滚动区域只有html元素高度最大值那部分,剩下部分无法显示出来。

代码

不加防抖节流
<!DOCTYPE html>
<html lang="en">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta name="format-detection" content="telephone=no, email=no">
</head>

<body>
    <header>
        <!-- <h1>无限滚动中的虚拟列表-防抖和节流(只渲染可视区域,dom元素可复用)</h1> -->
        <h1>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</h1>
    </header>
    </article>
    <article class="d-part d-effect">
        <style>
            .container {
    
    
                height: 600px;
                overflow: auto;
            }

            .item {
    
    
                min-height: 60px;
                border-bottom: 1px solid #cccccc;
                border-top: 1px solid #cccccc;
                width: 100%;
                text-align: center;
                background-color: darkgray;
                /* padding: 30px 0;
                box-sizing: border-box; */
            }
        </style>
        <div class="container">
            <div class="content">
                <div class="viewArea">
                    <div class="item">0</div>
                    <div class="item">1</div>
                    <div class="item">2</div>
                    <div class="item">3</div>
                    <div class="item">4</div>
                    <div class="item">5</div>
                    <div class="item">6</div>
                    <div class="item">7</div>
                    <div class="item">8</div>
                    <div class="item">9</div>
                </div>

            </div>
        </div>
        <script>
            var item = document.querySelector('.viewArea .item');  //需要渲染的单个列表元素
            var container = document.querySelector('.container');  //可视区域元素

            console.log(item);
            var start = 0; // 开始位置
            var pageSize = 10; // 每页展示的数据
            var total = 100000; //数据总长度

            // var itemHeight = 61; // 每个item的高度
            var itemStyle = getComputedStyle(item); // 获取元素最终样式
            var itemHeight = Number(itemStyle.height.split('px')[0]) + Number(itemStyle.borderTopWidth.split('px')[0]) + Number(itemStyle.borderBottomWidth.split('px')[0]); // 每个item的高度
            console.log('itemHeight', itemHeight);


            // 设置数据列表的总高度
            document.querySelector('.container .content').style.height = itemHeight * total + 'px';
            updateDom(start, pageSize, itemHeight, 0);
            
            //更新渲染列表函数
            function updateDom(start, pageSize, itemHeight, height) {
    
    
                document.querySelector('.container .content .viewArea').style.transform = 'translateY(' + height + 'px)';
                let all = document.querySelectorAll(' .viewArea .item'); // 获取所有渲染列表
                for (var i = start, itemIndex = 0, len = start + pageSize; i < len; i++, itemIndex++) {
    
    
                    var index = i % pageSize; // 计算出数据列表的下标
                    all[itemIndex].innerHTML = i;
                }
            }
            // 滚动处理函数
            function handleScroller() {
    
    
                var lastStart = 0; // 上次开始的位置
                return () => {
    
    
                    var currentScrollTop = container.scrollTop;
                    var fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight; 
                    var start = Math.floor(currentScrollTop / itemHeight);

                    if (lastStart !== start) {
    
    
                        lastStart = start;
                        updateDom(start, pageSize, itemHeight, fixedScrollTop);
                    }
                }
            }
            // // 防抖和节流
            // function throttle(fn, delay, atleast) {
    
    
            //     let timer = null;
            //     let rAFtimer = null;
            //     let previous = 0;

            //     return function () {
    
    
            //         let now = Date.now();
            //         if (now - previous > atleast) {
    
    
            //             console.log('now - previous > atleast');
            //             fn();
            //             previous = now;
            //         } else {
    
    
            //             if (delay > 20) {
    
    
            //                 console.log('delay > 20');
            //                 clearTimeout(timer);
            //                 timer = setTimeout(function () {
    
    
            //                     fn();
            //                     previous = 0;
            //                 }, delay);
            //             } else {
    
    
            //                 console.log('delay < 20');
            //                 cancelAnimationFrame(rAFtimer);
            //                 rAFtimer = requestAnimationFrame(function () {
    
    
            //                     requestIdleCallback(fn);
            //                 });
            //             }
            //         }
            //     }
            // }
            // document.querySelector('.container').addEventListener('scroll', throttle(handleScroller(), 16, 500), false);
            document.querySelector('.container').addEventListener('scroll', handleScroller(), false);
        </script>
    </article>
    </div>
</body>

</html>
加防抖节流
<!DOCTYPE html>
<html lang="en">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta name="format-detection" content="telephone=no, email=no">
</head>

<body>
    <header>
        <h1>无限滚动中的虚拟列表-防抖和节流(只渲染可视区域,dom元素可复用)</h1>
        <!-- <h1>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</h1> -->
    </header>
    </article>
    <article class="d-part d-effect">
        <style>
            .container {
    
    
                height: 600px;
                overflow: auto;
            }

            .item {
    
    
                min-height: 60px;
                border-bottom: 1px solid #cccccc;
                border-top: 1px solid #cccccc;
                width: 100%;
                text-align: center;
                background-color: darkgray;
                /* padding: 30px 0;
                box-sizing: border-box; */
            }
        </style>
        <div class="container">
            <div class="content">
                <div class="viewArea">
                    <div class="item">0</div>
                    <div class="item">1</div>
                    <div class="item">2</div>
                    <div class="item">3</div>
                    <div class="item">4</div>
                    <div class="item">5</div>
                    <div class="item">6</div>
                    <div class="item">7</div>
                    <div class="item">8</div>
                    <div class="item">9</div>
                </div>

            </div>
        </div>
        <script>
            var item = document.querySelector('.viewArea .item');
            var container = document.querySelector('.container');

            console.log(item);
            var start = 0; // 开始位置
            var pageSize = 10; // 每页展示的数据
            var total = 100000; //数据总长度

            // var itemHeight = 61; // 每个item的高度
            var itemStyle = getComputedStyle(item);
            var itemHeight = Number(itemStyle.height.split('px')[0]) + Number(itemStyle.borderTopWidth.split('px')[0]) + Number(itemStyle.borderBottomWidth.split('px')[0]); // 每个item的高度
            console.log('itemHeight', itemHeight);


            // 设置数据列表的总高度
            document.querySelector('.container .content').style.height = itemHeight * total + 'px';
            updateDom(start, pageSize, itemHeight, 0);
            function updateDom(start, pageSize, itemHeight, height) {
    
    
                document.querySelector('.container .content .viewArea').style.transform = 'translateY(' + height + 'px)';
                let all = document.querySelectorAll(' .viewArea .item');
                for (var i = start, itemIndex = 0, len = start + pageSize; i < len; i++, itemIndex++) {
    
    
                    var index = i % pageSize;
                    all[itemIndex].innerHTML = i;
                }
            }
            // 滚动处理函数
            function handleScroller() {
    
    
                var lastStart = 0; // 上次开始的位置
                return () => {
    
    
                    var currentScrollTop = container.scrollTop;
                    var fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight;
                    var start = Math.floor(currentScrollTop / itemHeight);

                    if (lastStart !== start) {
    
    
                        lastStart = start;
                        updateDom(start, pageSize, itemHeight, fixedScrollTop);
                    }
                }
            }
            // 防抖和节流
            function throttle(fn, delay, atleast) {
    
    
                let timer = null;
                let rAFtimer = null;
                let previous = 0;

                return function () {
    
    
                    let now = Date.now();
                    if (now - previous > atleast) {
    
    
                        console.log('now - previous > atleast');
                        fn();
                        previous = now;
                    } else {
    
    
                        if (delay > 20) {
    
    
                            console.log('delay > 20');
                            clearTimeout(timer);
                            timer = setTimeout(function () {
    
    
                                fn();
                                previous = 0;
                            }, delay);
                        } else {
    
    
                            console.log('delay < 20');
                            cancelAnimationFrame(rAFtimer);
                            rAFtimer = requestAnimationFrame(function () {
    
    
                                requestIdleCallback(fn);
                            });
                        }
                    }
                }
            }
            document.querySelector('.container').addEventListener('scroll', throttle(handleScroller(), 16, 500), false);
            // document.querySelector('.container').addEventListener('scroll', handleScroller(), false);
        </script>
    </article>
    </div>
</body>

</html>

猜你喜欢

转载自blog.csdn.net/weixin_43245095/article/details/108245106