关于javascript:2021-年了你还不会瀑布流布局三种靠谱JS方案-N种不靠谱CSS方案

32次阅读

共计 4091 个字符,预计需要花费 11 分钟才能阅读完成。

本着实用精力,咱们明天来分享一下 瀑布流布局 昨天有个小兄弟问我怎么做,我找了半天没找到,啊原来写在内网了)。演示地址: http://www.lilnong.top/static/html/waterfall.html

瀑布流布局是什么?

比如说 花瓣网 蘑菇街(我上面贴图了),这些网站在显示内容的时候就应用了瀑布流布局。

咱们也想做一个展现咱们设计稿(定宽,不定高 )的页面,瀑布流是很棒的一种计划。
瀑布流布局其外围是基于一个网格的布局,而且 每行蕴含的我的项目列表高度是随机的(随着本人内容动态变化高度),同时每个我的项目列表呈堆栈模式排列,最为要害的是,堆栈之间彼此之间没有多余的间距差存大。还是上图来看看咱们说的瀑布流布局是什么样子。

网站 蘑菇街 花瓣网 京东 VV
截图
计划 分通道 absolute

grid、inline、float 魔性计划

也算是纯 CSS 计划吧,实质上来讲是 依赖文档流,从左到右,从上到下。

计划 grid inline float bootstrap-grid
截图

能够看到在文档流布局中有非常明显的 的概念,当一个行被撑开就会留下空白,行与行不会重叠。这里 最魔性的就是 float 布局 了。

DOM 构造

div.list     // 设置 gird 或者 block,留神革除浮动
  div.item   // 设置为 inline 或者 float,使其能流动
    img      // 设置定宽,高度自适应,间距等。

grid 计划阐明

.wrap-waterfall--grid img{vertical-align: top;width: 100px}
.wrap-waterfall--grid .list{
    display: grid;
    grid-gap: 10px;
    /* 能够看到,网格大小,占据地位是须要提前设定的 */
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: minmax(50px, auto);
}

grid 在某些状况下会比 flex 好用。比如说须要 冲破行的限度,然而只实用于固定布局,如下图的布局,如果不应用 grid 你会如何实现呢?

网传有 gird 实现瀑布流布局的计划 ,然而我看了几个他们不是色块,就是图片变形、裁剪,计划是用 nth-child 定高, 太恐怖了吧

columns、flex CSS 实现 不靠谱计划

也是纯 CSS 计划,相比拟下面的计划而言,计划曾经能够承受,只是还有局部问题。

  • 程序是先垂直,后程度
  • (columns)兼容性问题
  • (flex)须要给一个固定高度,会呈现超出设定列,以及无奈充斥设定列。
计划 columns flex
截图

columns 计划

天生反对,只须要给父级设置即可 columns: 4; column-gap: 6px;

flex 计划

flex-flow: column wrap;height: 2300px; 默认状况下是程度排列,通过批改为垂直排列并且容许换行,之后把通过固定高度使内容换行。

absolute、通道 高度计算计划 靠谱计划

计划 absolute 取余分通道 计算高度分通道
头部截图

这里的计划就 靠谱起来了,能够满足咱们应用要求。

咱们来回顾一下咱们的需要:展现一些内容,内容有个性 定宽,不定高 。不定高个别是因为内容长度或者高度不统一导致的,常见内容又分为两种 文字和图片

  1. 文字的话,在没有异步字体的状况下,能够了解为同步就能够获取到盒子高度。
  2. 图片的话,因为加载是异步的,所以获取盒子的实在高度也是异步的。然而这里个别分为两种状况

    1. 无高度,那么能够通过 onload 来监听图片加载实现。等图片加载实现再去获取高度。
    2. 有高度,这种计划个别用在封面图、或者文章中,在上传图片的时候会保留原图尺寸,这个时候咱们就能够间接应用已有数据。

获取图片高度

// 用于获取图片的实在高度
naturalHeight: 1180
// 用于获取图片的实在宽度
naturalWidth: 1200

// 用户获取图片以后的渲染高度(会受 css 影响)height: 98
// 用户获取图片以后的渲染宽度(会受 css 影响)width: 100

// 可返回浏览器是否已实现对图像的加载。如果加载实现,则返回 true,否则返回 fasle。complete 属性
// 能够监听到图片加载实现的动作
onload 

基于下面的内容,那咱们能够先判断 complete 属性,

function getImageSize(img){if(img.complete){
        return Promise.resolve({
            naturalHeight: img.naturalHeight,
            naturalWidth: img.naturalWidth,
            height: img.height,
            width: img.width,
        })
    }else{return new Promise((resolve, reject)=>{img.addEventListener('load', ()=>{
                resolve({
                    naturalHeight: img.naturalHeight,
                    naturalWidth: img.naturalWidth,
                    height: img.height,
                    width: img.width,
                })
            })
        })
    }
}
/*
// 测试用例
el = document.createElement('img');
el.src = 'http://cors-www.lilnong.top/favicon.ico?'+Math.random()

getImageSize(el).then(console.log).catch(console.error)
setTimeout(()=>getImageSize(el).then(console.log).catch(console.error), 1000)
*/

absolute 计算高度计划

因为 一般的布局曾经无奈满足咱们的需要 ,所以咱们能够思考通过 position: absolute 来使 内容通过相对定位来显示

外围操作就是 保护每个元素的 left、top,而后应用 left 和 top 去渲染到正确地位。

getListPosition(){
    // 视口宽度 / 每列宽度 得出划分为几列
    let col = this.screenWidth / this.itemWidth >> 0;
    var arr = [];
    for(var i = 0; i < col; i++) arr.push({list: [],
        height: 0,
    })
    // 遍历所有元素
    this.listInfo.forEach((item,idx)=>{
        // 找到最低的一列
        var colIndex = 0;
        for(var i = 1; i < col; i++){if(arr[colIndex].height > arr[i].height){// colItem = arr[i]
                colIndex = i
            }
        }
        // 批改元素的信息
        // 所属列
        item.line = colIndex;
        // 计算之后的 top 间隔
        item.top = arr[colIndex].height+ 'px';
        // 计算之后的 left 间隔
        item.left = colIndex * (this.itemWidth + 10) + 'px'

        // 累加操作
        arr[colIndex].list.push(item);
        arr[colIndex].height += item.height + 10;
    })
    return arr
},

通过计算,咱们能够到,瀑布流布局下每个元素的地位,通过相对定位就能够实现。

依据下标,来渲染到不同的通道 idx % 4

因为上个计划用到了相对定位,那么有没有不必相对定位的计划呢?回到咱们的问题点上 定宽,不定高,那咱们齐全能够通过离开渲染放弃 absolute 来实现。

jsGroupList(){return this.list.reduce((s,n,idx)=>{
        // 依据下标,间接调配所属列
        s[idx % 4].push({idx: idx, item: n})
        return s
    }, [[],[],[],[],])
},

看结尾是实现相似的性能的,然而有一个弊病(快来评论区回复呀)。

通过高度计算,而后分通道,防止 absolute

因为上一个计划是按下标分类的,其实瀑布流是按高度分类的,所以咱们分类条件换成最低的列。

jsGroupHeightList(){
    var list = [{height: 0, list: []},{height: 0, list: []},
        {height: 0, list: []},{height: 0, list: []},
    ]
    // 遍历每个元素
    for(var i = 0; i < this.list.length; i++){
        // 当元素有大小的时候在进行操作。if(!this.listInfo[i].height) return list;
        // 默认第一个通道是最小高度列
        var minHeightItem = list[0];
        // 计算最小高度列
        list.forEach(v=>{if(v.height < minHeightItem.height) minHeightItem = v
        })
        // 把新的元素高度累加到列中。minHeightItem.height += this.listInfo[i].height
        // 把新的元素 push 到列中
        minHeightItem.list.push({idx: i, item: this.list[i]})
    }
    return list;
},

总结

好了,到这里我能想到的计划就都介绍了。你还有什么计划吗?咱们能够在评论区讨论一下可行性。接下来就是咱们的计划总结了。

计划 长处 毛病 点评
columns 实现简略、纯 CSS 计划 兼容性
flex 须要固定高度,填充难以管制等问题
float、inline、bootstrapGrid 没点大🧊都用不出这计划
grid 能够 nth-child 模仿实现、或者期待兼容性 masonry
absolute 成果好 JS 计算有限可能
js 一般通道 填充难以管制
js 优化通道 成果好、无相对定位 在呈现夸列等操作的时候不是很好管制

后记

  1. firefox 下的 CSS Grid 瀑布流布局(grid-template-rows: masonry)我竟然找到了,哈哈,一个简直不反对的属性。

正文完
 0