-
实现瀑布流,首先需要数据,这里我们制作一批假数据
- 安装json-server
npm install json-server
- 使用mock.js随机生成批量数据
npm init npm install mockjs
- 创建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 }
-
最后一步,开启json-server
json-server --watch --port 3003 --host 127.0.0.1 ./db.js
- 安装json-server
-
编写瀑布流核心代码
-
// 定义瀑布流算法函数,这里的参数是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; } } }
- 完整代码贴上(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>
- 至此,瀑布流完成
-
关于瀑布流的简单实现
最新推荐文章于 2023-01-05 17:46:34 发布