用原生js简简单单实现一个瀑布流布局并用防抖来优化窗口变化事件触发

前言

瀑布流布局是一种新型的,视觉效果优美的布局方式。他在杂乱的布局中又透着一定的美感,有一种乱中有序的感觉。这种不对齐的布局除去好看外也能够提高用户的留存率。今天就带大家用原生js实现一个简单的瀑布流布局。

实现

首先我们写一个这样的结构出来并加上样式,container内写上多个box,每一个box就是瀑布流中的一份子。

js 复制代码
    <style>
        *{
            margin: 0;
            padding: 0;
        }
    .box{
        float:left;
        padding: 5px;;
    }
    .box-img{
        width:150px;
        padding:5px;
        border:1px solid #ccc ;
    }
    img{width:100%;}
    #container{position: relative;}
    </style>
</head>
<body>
    <div id="container">
        <div class="box">
            <div class="box-img">
                <img src="./img/1.jpg" alt="">
            </div>
        </div>
    </div>
    <script src="./watterfall.js"></script>
</body>

这时的页面效果大致就是这个样子,每个box加了float:left所以box自动从左往右进行排列了,还给每个box-img写了一个宽度,高度不写让图片自身适应来防止图片变形。


接下来就来写主要的js部分了。首先明确下我们的目标,整个实现思路大概就是计算一行放几个box,然后第一行的box不去动他,就以第一行为基准,每次找到在y轴上最小的那个,将下一个box放在他的下面。

1.获取一行box数量和所有的num

我们的目标是传递一个父容器的id和一个所有子元素的类名然后动态的生成一个瀑布流的布局。

js 复制代码
function imgLocation(parent, content) {//当前有多少张图片要摆放
     var cparent = document.getElementById(parent)// 获取父元素
    var ccontent = document.querySelectorAll(`div[class=${content}]`)// 获取所有子元素
    var imgWidth = ccontent[0].offsetWidth
    var num = Math.floor(document.documentElement.clientWidth / imgWidth)// 计算一行能放多少个box
    cparent.style.width = imgWidth * num + 'px'// 给父容器设置合适的宽度
}

做完准备工作就开始进入正题吧

js 复制代码
function imgLocation(parent, content) {//当前有多少张图片要摆放
     var cparent = document.getElementById(parent)// 获取父元素
    var ccontent = document.querySelectorAll(`div[class=${content}]`)// 获取所有子元素
    var imgWidth = ccontent[0].offsetWidth
    var num = Math.floor(document.documentElement.clientWidth / imgWidth)// 计算一行能放多少个box
    cparent.style.width = imgWidth * num + 'px'// 给父容器设置合适的宽度
    // 分割线---------------------------------------------------------------
    var boxHeightArray = []// 储存底下所有图片的top值
    for (var i = 0; i < ccontent.length; i++) {
        if (i < num) {
            ccontent[i].style.position = null// 置空让第一行元素在页面大小缩放时第一行的top和left值保持正确
            boxHeightArray[i] = ccontent[i].offsetHeight// 第一行元素为基准,所以保持原本位置
        } else {
            var minHeight = Math.min.apply(null, boxHeightArray)
            var minIndex = boxHeightArray.indexOf(minHeight)
            // 找到top值最小的一个
            ccontent[i].style.position = 'absolute'
            ccontent[i].style.top = minHeight + 'px'
            ccontent[i].style.left = ccontent[minIndex].offsetLeft + 'px'
            boxHeightArray[minIndex] = boxHeightArray[minIndex] + ccontent[i].offsetHeight// 更新top值
        }
    }
}

至此我们在onload调用一下基本就算完成了

js 复制代码
window.onload = function () {
    imgLocation('container', 'box')
}

我们如果想要实现页面放大和缩小都能够保持一个正确的瀑布流布局。可以绑定resize事件

js 复制代码
window.addEventListener('resize', () => imgLocation('container', 'box'))

但是这样调用的话这个函数的触发会非常频繁

于是我们就能用到我们前几天聊到的防抖节流来进行一个优化。
面试官:既然聊到了闭包,那你手写一个防抖节流吧 - 掘金 (juejin.cn)

通过一个防抖函数使得每次窗口变化函数不会立刻执行,只会在变化结束后的500毫秒才调用该函数。这也是一个防抖的经典用法了。

js 复制代码
function debounce(fn, wait) {
    let timer = null;
    return function (...args) {
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.call(this, ...args)
        }, wait);
    }
}

最后的效果

相关推荐
gCode Teacher 格码致知1 分钟前
Javascript提高:get和post等请求,对于汉字和空格信息进行编码的原则-由Deepseek产生
开发语言·前端·javascript·node.js·jquery
竹林8183 分钟前
从ethers.js迁移到Viem:我在一个DeFi项目前端重构中踩过的坑
前端·javascript
像我这样帅的人丶你还23 分钟前
从交稿到甩锅预防:AI 前端流水线
前端·ai编程
想想弹幕会怎么做24 分钟前
如何构建一颗可交互的ui树?
前端
程序员陆业聪29 分钟前
我见过的最反直觉的 Android 架构问题:UseCase 越多,项目越烂
前端
NAGNIP33 分钟前
一文搞懂CNN经典架构-EfficientNet!
算法·面试
Arya_aa36 分钟前
网络:前端向后端发送网络请求渲染在页面上,将EasyMock中的信息用前端vue框架编写代码,最终展示在浏览器
前端·vue.js
LlNingyu37 分钟前
文艺复兴,什么是CSRF,常见形式(一)
前端·安全·web安全·csrf
晓131340 分钟前
React篇——第三章 状态管理之 Redux 篇
前端·javascript·react.js
子兮曰1 小时前
🚀24k Star 的 Pretext 为何突然爆火:它不是排版库,而是在重写 Web 文本测量
前端·javascript·github