JavaScript 实现动态加载数据瀑布流布局与滚动触发

作者:函数茶馆时间:2026-10-25 16:47:41浏览:471

本文详解使用 JavaScript 实现动态加载数据的瀑布流效果。核心功能包括滚动至底部自动加载下一页数据及图片逐渐显示。实现时需确保后端返回图片宽高,并处理 CSS 过渡生效的回流或定时器问题,以保证加载逻辑与视觉体验的稳定性。

本文分享了一个使用 JavaScript 实现动态加载数据的瀑布流布局的具体代码示例,供开发者参考。

功能实现目标

主要实现的功能如下:

1. 当用户滚动到页面底部时,自动加载并显示下一页的数据。 2. 图片以逐渐显示(渐显)的方式呈现。

HTML 结构准备

首先,需要构建基础的 HTML 结构。

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
            #wapper {
                width: 1200px;
                margin: 0 auto;
                position: relative;
            }
            .wr_item {
                position: absolute;
                overflow: hidden;
            }
            img {
                display: block;
                width: 100%;
                opacity: 0;
                transition: opacity 3s;
            }
        </style>
    </head>
    <body>
        <div id="wapper"></div>
        <script src="./scroll.js"></script>
        <script src="./data.js"></script>
        <script src="./warpper.js"></script>

        <script type="text/javascript">
            new Wapper({
                el: "wapper",
                el_itemClassName: "wr_item",
                colum: 8,
                gap: 10,
            }).init();
        </script>
    </body>
</html>

JavaScript 核心逻辑与注意事项

接下来是核心的 JavaScript 逻辑实现。

;
(function (doc) {
  // console.log('list', list);
  var Wapper = function (op) {
    this.el = doc.getElementById(op.el)
    this.el_itemClassName = op.el_itemClassName
    this.colum = op.colum
    this.gap = op.gap
    // 1.首先获取到每个照片外层盒子 也就是wr_item 的宽度
    this.wr_item_w = (this.el.offsetWidth - (this.colum - 1) * this.gap) / this.colum
    this.pageNum = 0
    this.hightArr = []
    this.pageSize = 4

  }
  Wapper.prototype = {
    init() {
      this.bindEvetn()
      this.getData()
    },
    getData() {
      // 这里默认一次获取30个照片 ,我这里了是假数据所以就不做别的了
      // 一般这里是向后端请求数据
      // list一共是有120
      const partList = getPartList(this.pageNum)
      if (partList) {
        this.render(partList)
        return true
      } else {
        return false
      }

    },
    render(partList) {
      // 只有数据存在才进行下面的操作
      if (partList) {
        partList.forEach((li, index) => {
          const o_item = document.createElement('div')
          // 这里要给o_item设置高度
          // 不要想着用img撑开,这样做会导致不能够获取到o_item的offsetWidth
          // 注意dom添加一个节点后,你是不能马上获取到其一些宽高的,
          // 所以后端在返回数据的时候要给出高度
          const imgW = li.width
          const imgH = li.height
          o_item.style.width = this.wr_item_w + 'px'
          // 高度等于 盒子宽度x图片高度/图片宽度
          const oitemH = (this.wr_item_w * imgH) / imgW
          o_item.style.height = oitemH + 'px'
          o_item.className = this.el_itemClassName
          const img = new Image()
          img.src = li.thumbURL

          // 注意这里好像不能直接设置透明度,最好加个定时器触发重绘
          // img.style.opacity = '1'

          o_item.appendChild(img)
          this.el.appendChild(o_item)
          // 设置第一行 
          // 必须是第一页的数据

          if (index < this.colum && this.pageNum === 0) {
            this.hightArr.push(o_item.offsetHeight)

            o_item.style.top = '0'

            if (index + 1 % this.colum === 0) {
              // 说明这是第一个
              o_item.style.left = '0'
            } else {
              o_item.style.left = index * (this.wr_item_w + this.gap) + 'px'
            }

          } else {
            const items = this.el.getElementsByClassName(this.el_itemClassName)
            const minIndex = getMinIdx(this.hightArr)
            const c_item = items[minIndex]
            o_item.style.left = c_item.offsetLeft + 'px'
            o_item.style.top = this.hightArr[minIndex] + this.gap + 'px'
            this.hightArr[minIndex] += (o_item.offsetHeight + this.gap)
          }
          img.style.opacity = '1'

        })
        console.log('this.hightArr', this.hightArr);
        this.el.style.height = this.hightArr[getMaxIdx(this.hightArr)] + 'px'
      }
    },
    bindEvetn() {
      var that = this
      window.onscroll = function () {
 
        if (getWindowHeight() + getScrollTop() === getHtmlScrollHeight()) {
          console.log(that.pageNum);

          that.pageNum++;
          let hasNext = that.getData()
          hasNext || that.pageNum--


        }

      }
    }
  }

  function getPartList(pageNum) {
    switch (pageNum) {
      case 0:
        return list.slice(0, 30)
        break;
      case 1:
        return list.slice(30, 60)
      case 2:
        return list.slice(60, 90)
      case 3:
        return list.slice(90, 120)
      default:
        return null
    }
  }

  // 找最小下标
  function getMinIdx(arr) {
    const minHeight = Math.min.apply(null, arr)
    return [].indexOf.call(arr, minHeight)
  }
  // 找最大
  function getMaxIdx(arr) {
    const maxHeight = Math.max.apply(null, arr)
    return [].indexOf.call(arr, maxHeight)
  }

  window.Wapper = Wapper

})(document)

代码中的 getWindowHeight() + getScrollTop() 用于检测浏览器是否滚动到了页面底部。

需要注意以下几点:后端返回的数据中,除了图片地址外,还必须包含每张图片的宽高信息。

在设置 CSS 过渡效果(如 opacity:1)时,必须确保过渡生效。这通常需要在设置过渡前触发一次回流操作(例如获取 offsetHeight),或者使用定时器包裹过渡逻辑,否则图片会直接显示而不会出现渐显效果。

后端每次返回的数据量应足够大。如果第一页数据过少,可能导致页面未出现滚动条,从而无法触发滚动事件。理想情况下,应自行编写逻辑判断,例如将请求参数封装到配置对象中,允许用户手动传入选项,后续可进一步完善此代码。

主要目的是帮助读者理解这一实现思路。

以上就是本文的全部内容,希望能为各位的学习提供帮助,欢迎大家继续支持本站。

作者最新文章