一个简单的虚拟滚动

一个刚入行没多久的菜比前端写的很简单的虚拟滚动,欢迎大佬们批评指正

先上效果

html部分

html 复制代码
    <div class="container">
      <div class="scroll-container" id="scroll-container">
        <div class="placeholder"></div>
        <div
          class="scroll-content"
          id="scroll-content"
          style="height: 1000000px"
        >
        </div>
      </div>
    </div>

css部分

css 复制代码
    .container {
      width: 100%;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .placeholder {
      width: 100%;
    }
    .scroll-container {
      width: 300px;
      height: 500px;
      border: 1px solid #ccc;
      overflow-y: scroll;
      position: relative;
    }

    .scroll-content {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
    }

scroll-container为列表的容器 scroll-content 为列表的可视部分,绝对定位展示在容器的最上层(表面) placeholder为一个实际上撑起列表高度的div(实际)

js部分

渲染前准备
js 复制代码
    const scrollContainer = document.getElementById("scroll-container");
    const scrollContent = document.getElementById("scroll-content");
    const placeholder = document.querySelector(".placeholder");
    const itemHeight = 50; // 每个项目的高度
    const totalItems = 100; // 总项目数
    const bufferItems = 5; // 缓冲项目数
    const visibleItems = Math.ceil(scrollContainer.clientHeight / itemHeight); // 可见的项目数
    const renderedItems = visibleItems + bufferItems; // 渲染的项目数
    placeholder.style.height = `${totalItems * itemHeight}px`;//给placeholder设宽度为总项目数*高度
渲染函数部分
js 复制代码
  // 生成虚拟滚动内容
  function renderItems(startIndex) {  //startIndex为应该渲染的第一项的索引
    startIndex = Math.max(0, Math.min(startIndex, totalItems - visibleItems));//边界检查 Math.min()是为了限制索引的不要超过最大项目数-可见项目数,Math.max防止索引小于0
       const fragment = document.createDocumentFragment();
    let i = startIndex;
    for (i; i < startIndex + renderedItems; i++) { //开始渲染 起始为startIndex,到startIndex + renderedItems终止
      if (i > totalItems - 1) break; //超过最大项数停止
      const item = document.createElement("div");
      item.className = "item";
      item.style.height = `${itemHeight}px`;
      item.style.lineHeight = `${itemHeight}px`;
      item.style.boxSizing = "border-box";
      item.style.padding = "0 10px";
      item.style.borderBottom = "1px solid #eee";
      item.textContent = `Item ${i + 1}`;
      fragment.appendChild(item);
    }
      scrollContent.style.height = `${(i - startIndex) * itemHeight}px`;//重设可见区域高度为实际渲染了多少项的高度
    scrollContent.innerHTML = "";
    scrollContent.appendChild(fragment);//重新渲染可见区域
  }

Math.min(startIndex, totalItems - visibleItems)是为了限制索引的不要超过最大项目数-可见项目数,比如一共100项,可见10项,拉到最下面也就从90项开始渲染,不能再多了。 Math.max是防止索引小于0,比如一共5项,可见项目10,如果只有Math.min就-5了,加个Math.max防止其小于0。 重设scrollContent.style.height是防止可见区域的高度大于剩余项的高度 比如还剩10项,renderedItems始终是15,如果不加的话高度就为15*50px了。

渲染部分
js 复制代码
   // 初始渲染
   renderItems(0);

   // 使用requestAnimationFrame优化滚动性能
   let isScrolling = false;
   scrollContainer.addEventListener("scroll", () => {
     if (!isScrolling) {
       window.requestAnimationFrame(() => {
         const scrollTop = scrollContainer.scrollTop;//滚动了多少
         const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight));//重新计算应渲染的下一项的索引
         renderItems(startIndex);
         scrollContent.style.transform = `translateY(${startIndex * itemHeight}px)`;//将可视区域向下移动 每往上滚了一个itemHeight就往下移一个itemHeight
         isScrolling = false;
       });
       isScrolling = true;
     }
   });

采用transform来实现滚动效果,原理大概如下 startIndex最开始是0 当scrollTop为50时意味着往上滚动了整整一项,那么这时该从第二项开始渲染了,startIndex变为1,同时translate 50px 让刚才滚出视口的那个渲染第一项的容器平移下来,用来渲染第二项。

相关推荐
清风细雨_林木木30 分钟前
Vue 2 项目中配置 Tailwind CSS 和 Font Awesome 的最佳实践
前端·css·vue.js
逊嘘34 分钟前
【Web前端开发】CSS基础
前端·css
小宁爱Python34 分钟前
深入掌握CSS Flex布局:从原理到实战
前端·javascript·css
Attacking-Coder44 分钟前
前端面试宝典---webpack面试题
前端·面试·webpack
极小狐1 小时前
极狐GitLab 容器镜像仓库功能介绍
java·前端·数据库·npm·gitlab
程序猿阿伟1 小时前
《Flutter社交应用暗黑奥秘:模式适配与色彩的艺术》
前端·flutter
rafael(一只小鱼)1 小时前
黑马点评实战笔记
前端·firefox
weifont1 小时前
React中的useSyncExternalStore使用
前端·javascript·react.js
初遇你时动了情1 小时前
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
前端·javascript·react.js
影子信息2 小时前
css 点击后改变样式
前端·css