关于瀑布流的简单实现

  1. 实现瀑布流,首先需要数据,这里我们制作一批假数据

    1. 安装json-server
      npm install json-server
    2. 使用mock.js随机生成批量数据
      npm init
      npm install mockjs
    3. 创建db.js(名字可以随便取 db意为database)
      const Mock = require('mockjs')
      
      const Random = Mock.Random
      
      const data = {
        waterfall: [] // 到时候发送请求需要向http://host:port/waterfall发送请求
      }
      
      module.exports = () => {
        // 随机生成20条数据,数据返回格式如下
        for(let i = 0; i < 20; i++) {
          data.waterfall.push({
            id: i+1,
            imgSrc: Random.image(),
            content: Random.cparagraph(3)
          })
        }
        return data
      }
    4. 最后一步,开启json-server

      json-server --watch --port 3003 --host 127.0.0.1 ./db.js
  2. 编写瀑布流核心代码

    1.   // 定义瀑布流算法函数,这里的参数是for循环的item数组的classname
        function fall(className:string):void {
          const gap = 20 // 设置每个Item之间的间距
          const scrollBarWidth = getScrollbarWidth();    // 获取滚动条的宽度
          const minItemWidth = 300; // 每一项的宽度,即当前每一个图片容器的宽度。保证每一列都是等宽不等高的。
          const pageWidth = window.innerWidth - scrollBarWidth; // 获取当前页面的宽度 = window.innerWidth - 滚动条的宽度
          const column = Math.floor(pageWidth / (minItemWidth + gap)); // 实际列数=页面宽度/(图片宽度+最小间距)
          const itemWidth = (pageWidth - column * gap - gap *1.5) / column;
          // const gap = (pageWidth - itemWidth * column) / column/1.3; // 计算真实间距 = (页面宽度- 图片宽度*实际列数)/实际列数/2
          console.log('column', column);
          
          // console.log(gap);
          
          const items:any = document.querySelectorAll(`.${className}`); // 获取所有的外层元素
          const heightArr:number[] = []; // 定义一个空数组,保存最低高度。
      
          // 获取滚动条的宽度(主要原理就是创建一个overflow为scroll的div,并通过固定公式获取滚动条宽度)
          function getScrollbarWidth() {
            const oDiv = document.createElement('div');//创建一个div
            // 给div设置样式。随便定义宽高,只要能获取到滚动条就可以
            oDiv.style.cssText = `width: 50px;height: 50px;overflow: scroll;`
            document.body.appendChild(oDiv);//把div添加到body中
            const scrollbarWidth = oDiv.offsetWidth - oDiv.clientWidth;// 使最大宽度和可视宽度相减,获得到滚动条宽度。
            oDiv.remove();//移除创建的div
            return scrollbarWidth;//返回滚动条宽度
          }
      
          if(pageWidth < 340) return
          for (let i = 0; i < items.length; i++) {
            // 遍历所有的外层容器
            let height = items[i].offsetHeight
            // console.log(height);
            
            // 如果当前处在第一行
            if (i < column) {
              // 直接设置元素距离上部的位置和距离左边的距离。
              items[i].style.cssText = `position:absolute;top: ${gap}px;left: ${(itemWidth + gap) * i + gap}px;width:${itemWidth}px;`;
              // 保存当前元素的高度。
              heightArr.push(height);
            } else {
              // 不是第一行的话,就进行比对。
              let minHeight:any = heightArr[0]; // 先保存第一项的高度
              let minIndex = 0; // 保存第一项的索引值
              for(let j = 0; j < heightArr.length; j++) {
                // 通过循环遍历比对,拿到最小值和最小值的索引。
                if(minHeight > heightArr[j]) {
                  minHeight = heightArr[j]
                  minIndex = j
                }
              }
              // 通过最小值为当前元素设置top值,通过索引为当前元素设置left值。
              items[i].style.cssText = `position:absolute;top: ${minHeight + gap * 2}px; left: ${(itemWidth + gap) * minIndex + gap}px;width: ${itemWidth}px;`;
              // 并修改当前索引的高度为当前元素的高度
              heightArr[minIndex] = minHeight + gap + height;
            }
          }
        }
    2. 完整代码贴上(Vue SFC):
      <!--  -->
      <template>
        <div class="waterfall-container">
          <!-- <img v-for="item in fallList" :src="item.imgSrc" alt=""> -->
          <div v-for='item in fallList' class="item-box">
            <div class="img-box">
              <img :src="item.imgSrc" @load="fall('item-box')">
            </div>
            <div class="content-box">{{item.content}}</div>
            <div class="id-box">{{item.id}}</div>
          </div>
        </div>
      </template>
      
      <script setup lang="ts">
        import { onBeforeMount, onUpdated, ref } from 'vue'
      
        const fallList = ref<Array<{id:number, imgSrc:string, content:string}>>([])
      
        const getFall = async () => {
          const data = await fetch('http://127.0.0.1:3003/waterfall')
          const res = await data.json()
          updateFallList(res)
        }
      
        const updateFallList = (list: Array<{id:number, imgSrc:string, content:string}>) => {
          fallList.value = list
          console.log(fallList);
          fall('item-box');
        }
      
        onBeforeMount(() => {
          getFall()
        })
      
        onUpdated(() => {
          fall('item-box');
        })
      
        // 定义瀑布流算法函数
        function fall(className:string):void {
          const gap = 20 // 设置每个Item之间的间距
          const scrollBarWidth = getScrollbarWidth();    // 获取滚动条的宽度
          const minItemWidth = 300; // 每一项的宽度,即当前每一个图片容器的宽度。保证每一列都是等宽不等高的。
          const pageWidth = window.innerWidth - scrollBarWidth; // 获取当前页面的宽度 = window.innerWidth - 滚动条的宽度
          const column = Math.floor(pageWidth / (minItemWidth + gap)); // 实际列数=页面宽度/(图片宽度+最小间距)
          const itemWidth = (pageWidth - column * gap - gap *1.5) / column;
          // const gap = (pageWidth - itemWidth * column) / column/1.3; // 计算真实间距 = (页面宽度- 图片宽度*实际列数)/实际列数/2
          console.log('column', column);
          
          // console.log(gap);
          
          const items:any = document.querySelectorAll(`.${className}`); // 获取所有的外层元素
          const heightArr:number[] = []; // 定义一个空数组,保存最低高度。
      
          // 获取滚动条的宽度
          function getScrollbarWidth() {
            const oDiv = document.createElement('div');//创建一个div
            // 给div设置样式。随便定义宽高,只要能获取到滚动条就可以
            oDiv.style.cssText = `width: 50px;height: 50px;overflow: scroll;`
            document.body.appendChild(oDiv);//把div添加到body中
            const scrollbarWidth = oDiv.offsetWidth - oDiv.clientWidth;// 使最大宽度和可视宽度相减,获得到滚动条宽度。
            oDiv.remove();//移除创建的div
            return scrollbarWidth;//返回滚动条宽度
          }
      
          if(pageWidth < 340) return
          for (let i = 0; i < items.length; i++) {
            // 遍历所有的外层容器
            let height = items[i].offsetHeight
            // console.log(height);
            
            // 如果当前处在第一行
            if (i < column) {
              // 直接设置元素距离上部的位置和距离左边的距离。
              items[i].style.cssText = `position:absolute;top: ${gap}px;left: ${(itemWidth + gap) * i + gap}px;width:${itemWidth}px;`;
              // 保存当前元素的高度。
              heightArr.push(height);
            } else {
              // 不是第一行的话,就进行比对。
              let minHeight:any = heightArr[0]; // 先保存第一项的高度
              let minIndex = 0; // 保存第一项的索引值
              for(let j = 0; j < heightArr.length; j++) {
                // 通过循环遍历比对,拿到最小值和最小值的索引。
                if(minHeight > heightArr[j]) {
                  minHeight = heightArr[j]
                  minIndex = j
                }
              }
              // 通过最小值为当前元素设置top值,通过索引为当前元素设置left值。
              items[i].style.cssText = `position:absolute;top: ${minHeight + gap * 2}px; left: ${(itemWidth + gap) * minIndex + gap}px;width: ${itemWidth}px;`;
              // 并修改当前索引的高度为当前元素的高度
              heightArr[minIndex] = minHeight + gap + height;
            }
          }
        }
          // 页面加载完成调用一次。
          // window.onload = () => {
          //   fall('item-box');
          // }
          // 页面尺寸发生改变再次调用。
          window.onresize = () => {
            fall('item-box');
          }
      
      </script>
      
      <style lang="less" scoped>
        * {
          box-sizing: border-box;
        }
        html, body {
          min-width: 340px;
        }
        .waterfall-container {
          position: relative;
          // min-width: 340px;
          // display: flex;
          // flex-wrap: wrap;
          // justify-content: space-around;
          border: 1px solid rgba(0, 0, 0, 0.5);
          .item-box {
            float: left;
            box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.3);
            width: 300px;
            padding: 20px;
            // margin: 10px;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            .img-box {
              flex: 3;
              height: 100%;
            }
            .content-box {
              flex: 1;
            }
            .id-box {
              flex: 1;
            }
          }
        }
      </style>
    3. 至此,瀑布流完成
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qd_sjy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值