阿里面试题:如何实现一个瀑布流布局

1,020 阅读5分钟

前言

  • 什么是瀑布流布局:

    瀑布流布局(Waterfall Layout),是一种常见的网页布局设计模式,特别是在基于feed或列表的界面中。它也被称为无限滚动或无限加载。 在传统的分页布局中,用户通过点击分页链接(例如“下一页”或“上一页”)来浏览列表中的项。相比之下,瀑布流布局会在用户滚动到页面底部时不断加载新内容,内容的加载看起来是无止境的。这种布局设计类似于瀑布的自然流动,新的内容不断被添加到页面的底部。*

  • 瀑布流布局的主要特征包括:

    1. 连续加载:新内容会动态加载到页面上,而不需要分页。
    2. 无限滚动:内容看起来是无止境的,新的项会不断被加载到页面上。
    3. 无分页链接:不需要分页链接,因为内容会动态加载。
    4. 动态内容加载:布局设计用来处理动态内容加载,新的内容会被添加到页面上。
  • 瀑布流布局的优点包括:

    1. 改善用户体验:用户可以不间断地浏览大量内容,提高了用户体验。
    2. 提高用户参与度:通过提供无止境的内容流,用户更容易参与和交互内容。
    3. 简化导航:用户不需要浏览分页链接,提高了浏览的便捷性。
  • 在设计瀑布流布局时,需要考虑以下因素:

    性能优化:确保布局的性能优化,因为连续加载可能会导致页面速度变慢。

    内容加载策略:确定最好的内容加载策略,例如批量加载或延迟加载。

    用户体验:确保布局设计提供了无缝的用户体验,减少中断或加载时间。

    通过将瀑布流布局整合到您的网页应用程序中,您可以为用户提供更好的体验!



现在在许多地方都用到了瀑布流布局,比如手机上的许多短视频软件,还有各种网站。 如下图

5a0731a17cb9b586a597ef488adeb4ba.png

代码实现

首先 我们先掌握要使用的几个基础知识

  1. js基础知识之获取容器的属性
        li = document.querySelector("li")
        console.log(li.offsetWidth); // 获取li的宽度
        console.log(li.offsetHeight); // 获取li的高度
        console.log(li.offsetLeft); // 获取li距离父元素的左侧距离
        console.log(li.offsetTop); // 获取li距离父元素的顶部距离  
  1. js基础之数组回调函数map

    JS 中的 map() 函数是 Array.prototype 中的一个方法,它用于将一个数组中的每个元素转换为另一个数组。 map() 函数的语法如下:

          arr.map(callbackFunction(currentValue, index, array))
    

    其中,callbackFunction 是一个函数,它将被应用于数组中的每个元素。currentValue 是当前元素的值,index 是当前元素的索引,array 是原始数组。 map() 函数返回一个新数组,包含了原始数组中每个元素经过转换后的结果。 例如,假设我们有一个数组 [1, 2, 3, 4, 5],我们想要将每个元素乘以 2,得到一个新的数组 [2, 4, 6, 8, 10]。我们可以使用 map() 函数来实现:

          const originalArray = [1, 2, 3, 4, 5];
          const newArray = originalArray.map(x => x * 2);
          console.log(newArray); // [2, 4, 6, 8, 10]
    

    在这个例子中,map() 函数将每个元素乘以 2,并将结果返回到一个新的数组中。

    map() 函数的优点包括:

    简洁易用:map() 函数提供了一种简洁易用的方式来将数组中的每个元素转换为另一个数组。 链式调用:map() 函数可以与其他数组方法链式调用,例如 filter()、reduce() 等。 不改变原始数组:map() 函数返回一个新的数组,不会改变原始数组。 map() 函数的应用场景包括:

    数据转换:将数组中的每个元素转换为另一个格式或类型。 数据过滤:将数组中的每个元素过滤掉不需要的元素。 数据聚合:将数组中的每个元素聚合成一个新的值。 总之,map() 函数是一种非常有用的数组方法,它可以帮助我们简洁地将数组中的每个元素转换为另一个数组。

HTML代码

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        #container {
            position: relative;
        }

        .box {
            float: left;
            padding: 5px;
        }

        .box-img {
            width: 150px;
            padding: 5px;
            border: 1px solid #aaa;
        }

        img {
            width: 100%;
        }
    </style>
    <script src="./index.js"></script>
</head>

<body>
    <!-- 瀑布流布局 -->
    <div id="container">
        <!-- 20个.box -->
    </div>
</body>

</html>

JS代码部分

  1. 确定第一行能放n张图需考虑到设备的不同

    • 获取屏幕的宽度
    • 读取图片的宽度

    该部分代码如下

        var cParent = document.getElementById(parent)
        var cChild = cParent.getElementsByClassName(child)
        var screenWidth = window.innerWidth // 获取屏幕宽度
        var imgWidth = cChild[0].offsetWidth // 获取图片宽度
        var num = Math.floor(screenWidth / imgWidth) // 计算第一行能放几张图   // 使用Math.floor()向下取整
        cParent.style.width = `${num * imgWidth}px` // 设置容器的宽度  
  1. 操作第n+1张图 摆放位置到高度最小的一列

    • 获取每一列的高度
    • 更新列的高度

    该部分代码如下

        var boxHeightArr = []
        for (var i = 0; i < cChild.length; i++) {    // 遍历所有图片
            if (i < num) { // 第一行
                // cChild[i].offsetHeight
                boxHeightArr.push(cChild[i].offsetHeight)  // 将第一行的高度存入数组  
            } else {
                var minHeight = Math.min(...boxHeightArr)
                var minIndex = boxHeightArr.indexOf(minHeight)

                // 摆放图片
                cChild[i].style.position = 'absolute'
                cChild[i].style.top = minHeight + 'px' // 距离顶部的距离    
                cChild[i].style.left = cChild[minIndex].offsetLeft + 'px'    // 距离左侧的距离 

                // 更新高度数组
                boxHeightArr[minIndex] = boxHeightArr[minIndex] + cChild[i].offsetHeight
            }
        }

JS完整代码

window.onload = function () {
    // 确定第一行能放几张图
    // 1. 获取屏幕的宽度
    // 2. 获取图片的宽度

    // 操作第 n+1 张 摆放它的位置 放在高度最小的那一列 
    // 1. 获取所有列的高度    
    // 2. 更新列的高度

    imgLocation('container', 'box')
    function imgLocation(parent, child) {
        var cParent = document.getElementById(parent)
        var cChild = cParent.getElementsByClassName(child)
        var screenWidth = window.innerWidth // 获取屏幕宽度
        var imgWidth = cChild[0].offsetWidth // 获取图片宽度
        var num = Math.floor(screenWidth / imgWidth) // 计算第一行能放几张图   // 使用Math.floor()向下取整
        cParent.style.width = `${num * imgWidth}px` // 设置容器的宽度  

        
        //操作第 num+1 张
        var boxHeightArr = []
        for (var i = 0; i < cChild.length; i++) {    // 遍历所有图片
            if (i < num) { // 第一行
                // cChild[i].offsetHeight
                boxHeightArr.push(cChild[i].offsetHeight)  // 将第一行的高度存入数组  
            } else {
                var minHeight = Math.min(...boxHeightArr)
                var minIndex = boxHeightArr.indexOf(minHeight)

                // 摆放图片
                cChild[i].style.position = 'absolute'
                cChild[i].style.top = minHeight + 'px' // 距离顶部的距离    
                cChild[i].style.left = cChild[minIndex].offsetLeft + 'px'    // 距离左侧的距离 

                // 更新高度数组
                boxHeightArr[minIndex] = boxHeightArr[minIndex] + cChild[i].offsetHeight
            }
        }
        console.log(cChild);
    }
}

效果

image.png

对瀑布流布局的实现就结束了,希望对你有帮助。