瀑布流布局 不到30行代码实现(JavaScript + absolute)支持懒加载

最后效果


@


前言

瀑布流 网站页面布局
瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格。

简单来说: 瀑布流式布局其实式一种网页的样式效果,简单来说就是一种单个宽度固定单个图片高度根据内容适应多个块自动换行,但是紧凑拼在一起的效果。既可以使用纯css快速的达到这种样式效果,也可以通过JavaScript辅助,实现更丰富合理的效果


一、使用css实现瀑布流布局

1.flex 布局

使用flex布局,将容器设置为flex布局,设置主轴方向为列方向,允许换行,容器设定固定高度(这样才会自动换列),然后子元素设置宽度为对应比例,在某一列填满时会自动往下一列填充,并且每一列元素填充十分紧凑****使用flex布局实现的基础的瀑布式布局
优化方向:使用order属性优化排序方向

2.column-count 多栏布局

类似的还有使用 多栏布局,使用column-count属性,设置多栏布局,效果跟flex布局类似,排列方向也是从上到下,从左到右

3.grid 网格布局

二、结合JavaScript的瀑布流布局实现

1.推荐原因

瀑布流式布局经常被使用在图片的展示页面,大量的图片资源加载,如果只用css样式的话必须一次性加载所有图片,我们期待的是瀑布流懒加载结合在一起,在滚动视图的同时,不断加载下面的内容

2.实现步骤

如果需要结果的话可以直接跳到文章最后

a.初步实现:结合JavaScript实现瀑布流

原理:

  1. 子元素绝对定位
  2. 通过javascript设置偏移量

html代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./index.css">
</head>

<body>
    <div id="app"></div>
    <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script> -->
    <!-- <script src="./src/vue.min.js"></script> -->
    <script src="./util.js"></script>
    <script src="./index.js"></script>
</body>

</html>

css 代码:

html {
    background-color: black;
}

#app {
    position: relative;
    width: 100%;
    margin: auto;
}

.item {
    position: absolute;
    border-radius: 20px;
    border: 10px solid black;
    box-sizing: border-box;
    text-align: center;
    color: white;
}

utils.js 代码:

const randomImgSize = [	// 这里是模拟各种尺寸的图片
    {
        width:1920,
        height:1080
    },{
        width:640,
        height:720
    },{
        width:720,
        height:680
    },{
        width:720,
        height:1920
    },{
        width:900,
        height:600
    },{
        width:400,
        height:300
    },{
        width:2048,
        height:1920
    },{
        width:700,
        height:900
    },{
        width:1600,
        height:900
    },{
        width:200,
        height:200
    }
]

function randomColor(){
    let r = Math.floor(Math.random() * 255)
    let g = Math.floor(Math.random() * 255)
    let b = Math.floor(Math.random() * 255)
    return `rgb(${r},${g},${b})`
}

index.js 代码

   		// 创建元素函数
       function generateBlock(size,color){
           let dom = document.createElement('div')
           dom.setAttribute('class','item')
           dom.style.width = '240px'			// 这里默认写死宽度,后面会优化
           dom.style.height = `${240/size.width * size.height}px`		// 由于这里没有图片资源,高度模拟计算
           // 图片资源的话不需要直接width:100% 高度会自适应
           dom.style.backgroundColor = color
           // 颜色也是随机生成而已
           document.getElementById('app').appendChild(dom)
           return dom
       }
       // 元素统计数组
       let domArr = []
       // 元素高度数组
       let heightArr = new Array(4)	// 创建对应列数,存储每一列高度,用来设置偏移量
       heightArr.fill(0)
       // 常规不考虑高度不整齐逻辑
       randomImgSize.forEach(ele=>{
           let dom =generateBlock(ele,randomColor())
           domArr.push(dom)		// 通过transform 属性可以一次设置,不用设置left和top,不过也一样
           dom.style.transform =  `translate(${(domArr.length-1)%4 * 100}%, ${heightArr[domArr.length%4]}px)`
           heightArr[domArr.length%4] += dom.offsetHeight
       })

实现内容:

  1. 初步实现瀑布流布局,正确计算每一个块(图片)的正确位置

  2. 每个块只是死板的按照从左到右,从上到下的顺序,可以优化:下一个块生成位置为当前列最低高度位置,自动填补进去

  3. 没有实现懒加载

    基础的js结合实现瀑布流

b.稍微优化:每次自动填补到高度最低

index.js

        // 创建元素函数
        function generateBlock(size,color){
            let dom = document.createElement('div')
            dom.setAttribute('class','item')
            dom.style.width = '240px'
            dom.style.height = `${240/size.width * size.height}px`
            dom.style.backgroundColor = color
            document.getElementById('app').appendChild(dom)
            return dom
        }
        // 元素统计数组
        // let domArr = []
        // 元素高度数组
        let heightArr = new Array(4)
        heightArr.fill(0)
        // 常规不考虑高度不整齐逻辑
        // randomImgSize.forEach(ele=>{
        //     let dom =generateBlock(ele,randomColor())
        //     domArr.push(dom)
        //     dom.style.transform =  `translate(${(domArr.length-1)%4 * 100}%, ${heightArr[domArr.length%4]}px)`
        //     heightArr[domArr.length%4] += dom.offsetHeight
        // })

        // 考虑高度不整齐逻辑
        randomImgSize.forEach(ele=>{
            let dom =generateBlock(ele,randomColor())
            let minHeight=  Math.min(...heightArr)
            let minHeightIndex = heightArr.indexOf(minHeight)
            dom.style.transform =  `translate(${minHeightIndex * 100}%, ${minHeight}px)`
            heightArr[minHeightIndex] += dom.offsetHeight
        })

实现效果:
优化高度填充对比

  1. 可以看到效果对比之前还是要好很多,不会出现极高或者极低的情况,而且代码还简化了,不需要数组来缓存元素,直接通过长度为4 的高度数组,查询应该插入到哪个列
  2. 对应列的偏移量:x为 100% * 对应列下标,y为对应列下标高度
  3. 但是宽度还是写死的,而且没有实现懒加载

b.最终结果 (不到三十行代码)支持懒加载

index.js

		// 响应式
        const columns = 3
        let tempArr = new Array(columns)  // 创建数组用来缓存高度
        tempArr.fill(0)
        function create(){
            let observer = new IntersectionObserver(callback)
            function callback(entries){
                if(entries[0].isIntersecting){  // 如果已进入视图,停止监听,并且生成新的元素
                    observer.unobserve(dom)
                    create()
                }
            }
            function generateBlock(size,color){
                let dom = document.createElement('div')
                dom.setAttribute('class','item')
                dom.style.width = `${Math.floor(100/columns)}%`         // 宽度为百分比
                dom.style.height = `${300/size.width * size.height}px`  // 300为默认高度
                dom.style.backgroundColor = color
                document.getElementById('app').appendChild(dom)
                return dom
            }
            let donSizeIndex = Math.floor(Math.random() * randomImgSize.length) // 随机生成大小(模拟不同尺寸的图片)
            let dom =generateBlock(randomImgSize[donSizeIndex],randomColor())   // 生成随机块
            let minHeight=  Math.min(...tempArr)                                // 找到对应列最小高度的值
            let minHeightIndex = tempArr.indexOf(minHeight)                     // 找到对应列最小高度的下标
            dom.style.transform =  `translate(${minHeightIndex*100}%, ${minHeight}px)`      // 根据下标进行变换,变换宽度为偏移多少个下标,上下为该下标所有高度
            tempArr[minHeightIndex] += dom.offsetHeight         // 对应下标增加高度
            observer.observe(dom)   // 观察该元素用作懒加载
        }
        create()

实现效果:
在这里插入图片描述
实现比较简单其实,主要是javascript操作样式,设置对应transform属性,通过全局设置一个高度数组,每次添加元素时获取每一列高度,选取最小高度列,将元素偏移量设置正常,并且通过intersectionObserver对象,监听与视口交叉事件,每次触发时,生成新的元素

同理可以拓展到加载图片..等等


总结

以上就是今天要的结合JavaScript实现图片瀑布流式布局+懒加载的实现过程,本文仅仅简单介绍了瀑布流布局相关设计思路,如果需要增加其他功能可以在该demo的基础上进行修改

觉得有帮助的话,点赞关注支持一下,b站前端同名分享号:猿油站

posted @ 2022-12-07 16:26  三十男  阅读(268)  评论(0编辑  收藏  举报