likes
comments
collection
share

阿里面试官问:怎么实现一个瀑布流布局?

作者站长头像
站长
· 阅读数 27

瀑布流布局

瀑布流布局是一种独具特色的网页布局形式。在这种布局中,页面上的元素如图片、卡片等会以参差不齐的方式排列,每行的长度并不相同,形成一种错落有致的视觉效果。当用户滚动页面时,新的元素会源源不断地动态加载,就像瀑布一样不断流淌,给人以流畅且富有吸引力的感受。它能够充分利用页面空间,灵活地展示大量内容,尤其适合用于图片分享、产品展示等网站,为用户带来独特的浏览体验。同时,这种布局方式也为网页设计带来了更多的创意和可能性。

阿里面试官问:怎么实现一个瀑布流布局?

阿里面试官:怎么实现一个瀑布流布局?

瀑布流布局的大体思路是这样的:

  1. 获取页面大小将页面划分为多个列。
  2. 每列中的元素依次排列,通过设置元素的浮动(通常为左浮动),使它们在同一列中依次排列。
  3. 实时监测每列的高度,并找出当前高度最小的列,将新元素添加到高度最小的列中。

实现步骤

html部分

<div id="container">
    <div class="box">
        <div class="box-img">
            <img src="./img/1.webp" alt="">
        </div>
    </div>
    .....
</div>

我们可以在类名为container的盒子中放入多个box类盒子,用来测试我们的瀑布流布局是否有问题。

css部分

* {
    margin: 0;
    padding: 0;
}
#container {
    position: relative;
}
.box {
    float: left;
    padding: 5px;
}
.box-img {
    width: 150px;
    padding: 5px;
}
img {
    width: 100%;
}
  • 我们通过浮动让图片依次排列在一行里。
  • 设置内边距,让图片之间隔开一些距离
  • 固定每一个box的宽度,但是不固定高度。这样可以让图片等比列缩放,防止图片造成变形。

为什么我们要给#container 选择器的样式添加position属性,并且属性值为relative?

我们将在js部分解答这个问题。

JavaScript部分

我们通过js实现获取用户屏幕的宽度,决定一行能放下几张图;操作下一张图,放到上一行最矮的列下面。

我们可以通过封装在一个imgLocation()函数内实现这些功能。

imgLocation('container', 'box')
function imgLocation(parent, content) {}
  • 获取id为container的元素

     var cparent = document.getElementById(parent)
    
  • 获取box数量

    //获得box,方法一
    var ccontainer = document.querySelectorAll('.box')
    //获得box,方法二
    var ccontainer = ocument.querySelectorAll('#contain .box')
    //获取box,方法三(自己打造方法)
    var ccontent = getChildElement(cparent, content)
    
    • 方法一:具有局限性,可能会获取到不是我们需要的box。

    • 方法二:通过#contain .box可以获取到选择器为#contain 下的类名为.box 的元素,这样就不会获取到额外的box。但是这样并不灵活,如果我们不用box当作放置图片的盒子,我们就需要找到这个方法并且修改内容。

    • 方法三:我们可以定义一个方法获取。

      function getChildElement(parent, child) {
          //获取parent中所有的child
          var childArr = []
          var allChild = parent.getElementsByTagName('*')
          //挑出来所有的box
          for (var i = 0; i < allChild.length; i++) {
              if (allChild[i].className == child) {
                  childArr.push(allChild[i])
              }
          }
          return childArr
      }
      
      1. child== content == ‘box’,这样我们只需要修改imgLocation('container', 'box')里的值就好了。

        imgLocation('container', 'box')
        function imgLocation(parent, content) {
            var cparent = document.getElementById(parent)
            var ccontent = getChildElement(cparent, content)
        }
        function getChildElement(parent, child) {}
        
      2. 创建一个childArr数组获取我们需要的box;设置allChild保存用getElementsByTagName()方法获取#contain标签中所有元素的集合。下图为allChild的内容(没有截全)。

        阿里面试官问:怎么实现一个瀑布流布局?

        我们通过遍历并且判断allChild[i].className == ‘box’,如果为true则通过push()方法加入childArr数组,否则继续遍历。

      3. 最后返回childArr数组。

  • 动态获取页面一行可以放置几个box。

    //获取每一个box的宽度
    var imgWidth = ccontent[0].offsetWidth
    //用Math.floor()向下取整
    var num = Math.floor(document.documentElement.clientWidth / imgWidth)
    //通过一行最多放置的box数动态设置#container标签的宽度
    cparent.style.width = `${imgWidth * num}px`
    

    num = (页面的宽度 / box的宽度)向下取整,num的值就是一行可以放下图片数量。

  • 操作图片,放到上一行最矮的列下面。

    var BoxHeightArr = []
    for (var i = 0; i < ccontent.length; i++) {
        if (i < num) {
            //都是第一行的图      
            BoxHeightArr.push(ccontent[i].offsetHeight)
        }
        else { 
            var minHeight = Math.min.apply(null, BoxHeightArr)
            var minIndex = BoxHeightArr.indexOf(minHeight)
            //摆放图片的位置
            ccontent[i].style.position = 'absolute'
            ccontent[i].style.top = minHeight + "px"
            ccontent[i].style.left = ccontent[minIndex].offsetLeft + "px"
            //更新这一列的高度
            BoxHeightArr[minIndex] = BoxHeightArr[minIndex] + ccontent[i].offsetHeight
        }
    }
    

    创建BoxHeightArr数组保存每一列的高度。

    从i=0,开始遍历到i < ccontent.length。

    当i < num,就意味着都是可以排列在页面第一行,让BoxHeightArr数组保存其高度。

    当i >= num,就意味着需要找到最矮的列后让该box补到最矮的那一列。

    • 通过Math.min.apply(null, BoxHeightArr)获取数组最小高度。

    • 通过BoxHeightArr.indexOf(minHeight)获取最小高度所在列。

    • 在找到要添加的列后,摆放位置。

      ccontent[i].style.position = 'absolute'
      //设置距离页面上边界的距离
      ccontent[i].style.top = minHeight + "px"
      //设置距离页面左边界的距离
      ccontent[i].style.left = ccontent[minIndex].offsetLeft + "px"
      

      这里就可以知道为什么要给#container 选择器的样式添加position属性,并且属性值为relative。因为要给图片进行定位。

    最后更新每列的高度。

小插曲——函数提升

imgLocation('container', 'box')
function imgLocation(parent, content) {}

因为代码是从上向下开始运行的。但是在运行到imgLocation()函数时,我们并没有定义它,而是在imgLocation()函数之后才定义。但是这样运行并没有报错。这是为什么呢?

是因为函数提升,它是指在代码执行之前,函数声明会被提升到当前作用域的顶部。这意味着在函数调用之前,即使函数定义在调用之后,也可以正常执行函数。

小结

我们现在了解了瀑布流布局是什么并且知道了如何实现。当面说被问到就不会慌张了。

转载自:https://juejin.cn/post/7361231869587226635
评论
请登录