如何实现瀑布流布局

184 阅读6分钟

前言

大家好!👋

在这个数字世界里,瀑布流布局以其独特的魅力成为了展示内容的一种流行方式。它不仅让页面看起来更加生动有趣,还能适应不同设备的屏幕大小。今天,就让我们一起探索如何用前端技术打造一个属于自己的瀑布流布局。

样式展示: image.png

正文

知识点

id&class

id 是页面上唯一的元素标识符,用于指向特定元素或在JavaScript中定位;class 可以被多个元素共享,用于应用一组样式。id 选择器用 # 表示,class 选择器用 . 表示。

浮动布局

  1. 脱离文档流:使用float属性的元素会脱离正常的文档流,这意味着它不会占据文档流中的正常位置。
  2. 左右浮动:通过设置float: left;float: right;,元素可以向左或向右浮动。
  3. 环绕内容:浮动元素周围的文本和其他元素可以环绕浮动元素,形成多列布局。
  4. 清除浮动:使用clear属性可以清除浮动的影响,防止元素被浮动元素覆盖。
  5. 布局控制:浮动布局可以用于控制元素的布局,但需要小心处理,以避免布局问题。

.offsetWidth

返回一个元素的布局宽度,包括元素的边框(border)、内边距(padding)和内容区域(content area),但不包括外边距(margin)

offsetWidth 返回的是一个整数值,表示元素的宽度,单位是像素(px)、

摆放图片位置

  1. ccontent[i].style.position = 'absolute';

    • 这行代码将图片元素(ccontent[i])的position 属性设置为 absolute。所以该元素将从文档流中脱离出来,并且可以使用 toprightbottomleft 等属性来绝对定位它的位置。
  2. ccontent[i].style.top = minHeight + 'px';

    • 设置图片元素的 top 属性,使其垂直位置相对于包含它的最近的已定位(非 static)祖先元素。minHeight 是当前找到的最小高度值,这通常是前一行中最低的列的高度。通过将图片放在这个高度,新的图片将被放置在前一行的最低点下方。
  3. ccontent[i].style.left = ccontent[minIndex].offsetLeft + 'px';

    • 同top,ccontent[minIndex].offsetLeft 是前一行中高度最低的列的第一个元素的水平偏移量。通过使用这个值,新的图片将被放置在前一行最矮列的开始位置,从而实现水平对齐。

设置容器宽度

cparent.style.width = {imgWidth *num}px

用js代码中的例子进行解释:

  • 这里使用模板字符串将 imgWidth 和 num 的值嵌入到字符串中,计算出容器的总宽度。
  • 设置 cparent 元素的 style.width 属性,将其宽度设置为计算出的结果,单位是像素(px)

js代码核心部分

var BoxHeightArr = [];
    for (let i = 0; i < ccontent.length; i++) {
        if (i < num) {
            BoxHeightArr[i] = 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] += ccontent[i].offsetHeight;

        }
    }

这段JavaScript代码的作用是遍历所有的图片元素,计算并设置每张图片的确切位置。下面是对代码的逐行解释:

  1. var BoxHeightArr = [];

    • 初始化一个数组 BoxHeightArr,存储每一列最矮矮子的高度。
  2. for (let i = 0; i < ccontent.length; i++) { ... }

    • 循环遍历所有的图片元素(ccontent 数组中的元素)。
  3. if (i < num) { ... }

    • 条件判断,如果当前索引 i 小于每行可以放置的图片数量 num,则执行。
  4. BoxHeightArr[i] = ccontent[i].offsetHeight;

    • 将第一行的图片元素的高度赋值给 BoxHeightArr 数组对应的位置。因为第一行的图片直接显示,无需进行位置计算。
  5. var minHeight = Math.min.apply(null, BoxHeightArr);

    • 使用 Math.min 函数和 apply 方法找出 BoxHeightArr 数组中的最小值,即当前最矮的列的高度。
  6. var minIndex = BoxHeightArr.indexOf(minHeight);

    • 这行代码获取最小高度在 BoxHeightArr 数组中的索引,即最矮列的位置。
  7. ccontent[i].style.position = 'absolute';

    • 将当前图片元素的CSS position 属性设置为 absolute,这样可以通过 top 和 left 属性来绝对定位它。
  8. ccontent[i].style.top = minHeight + 'px';

    • 设置 top 属性,使其垂直位置位于最矮列的当前高度之上。
  9. ccontent[i].style.left = ccontent[minIndex].offsetLeft + 'px';

    • 设置left 属性,使其水平位置与最矮列的第一个图片元素的左边距相同,实现水平对齐。
  10. BoxHeightArr[minIndex] += ccontent[i].offsetHeight;

-   更新最矮列的高度,将当前图片的高度加到 `BoxHeightArr` 数组对应索引的位置。

这段代码的目的是实现一个动态的图片布局,其中图片会根据前一行中最矮的列的高度来确定自己的位置,从而创建出视觉上更加平衡和美观的瀑布流效果。

HTML

HTML主要代码

<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: 4px solid #debe80;
        }

        img {
            width: 100%;/* box-img的100% 高也自动收缩,图片不失真*/
        }
    </style>
</head>

<body>

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

        <div class="box">
            <div class="box-img">
                <img src="./img/2.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/3.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/4.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/5.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/6.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/7.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/8.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/9.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/10.webp" alt="">
            </div>
        </div>

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

        <div class="box">
            <div class="box-img">
                <img src="./img/2.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/3.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/4.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/5.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/6.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/7.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/8.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/9.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/10.webp" alt="">
            </div>
        </div>

        <div class="box">
            <div class="box-img">
                <img src="./img/11.webp" alt="">
            </div>
        </div>
    </div>

    <script src="index.js"></script>
</body>

</html>

JS

javascript部分是大头,写js的主要目标就是,获取到用户屏幕的宽度,这样就能决定一行能放几张图,再者是操作下一张图,放到上一行最矮的列下面

JavaScript完整代码:

function imgLocation(parent, content) {
    var cparent = document.getElementById(parent);//parent在这里相当于'container'

    // 有多少张图片
    // var ccontent = document.querySelectorAll('.box');//把类名为box全部获取,但可能别的类名也为box所以这里不采用
    // var ccontent = document.querySelectorAll('#container .box');//取到的是container的box,好用!但是我们选择手搓,看辅助函数getChildElement()
    var ccontent = getChildElement(cparent, content);
   

    // 获取每一个box的宽度
    var imgWidth = ccontent[0].offsetWidth;//获取容器的宽度,任何标签的自带的属性

    // 屏幕一行能放下几个图片  向下取整
    var num = Math.floor(document.documentElement.clientWidth / imgWidth);

    //设置container的宽度
    cparent.style.width = `{imgWidth *num}px`;

    // 要操作的是哪一张 num+1,取到每一列的高度
    var BoxHeightArr = [];
    for (let i = 0; i < ccontent.length; i++) {
        if (i < num) {
            // 第一行
            BoxHeightArr[i] = 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] += ccontent[i].offsetHeight;

           
        }
    }
}
//辅助函数getChildElement()
function getChildElement(parent, child) {
    // 获取parent中所有的child
    var childArr = [];
    // 返回的是数组结构
    var allChild = parent.getElementsByTagName('*');//parent里的所有的标签
    // 挑出所有Child中的box
    for (let i = 0; i < allChild.length; i++) {
        if (allChild[i].className == child) {//判断类名是否含有形参child
            childArr.push(allChild[i]);//尾部增加进去
        }
    }
    return childArr;

}

imgLocation('container', 'box');

结语

希望你喜欢这次的分享,也许这些小技巧能给你的项目带来一些灵感。如果你有任何问题或建议,欢迎私信留言!期待与你的每一次交流,让再次感谢你的阅读,我们下次见!💬👋