所有分类
  • 所有分类
  • 织梦模板

用JS或CSS如何实现瀑布流布局 3种方法介绍

介绍一下三种靠谱JS方案,以及N种不靠谱CSS方案。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

本着实用精神,我们今天来分享一下瀑布流布局(昨天有个小兄弟问我怎么做,我找了半天没找到,啊原来写在内网了)。

瀑布流布局是什么?

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

我们也想做一个展示我们设计稿(定宽,不定高)的页面,瀑布流是很棒的一种方案。

瀑布流布局其核心是基于一个网格的布局,而且每行包含的项目列表高度是随机的(随着自己内容动态变化高度),同时每个项目列表呈堆栈形式排列,最为关键的是,堆栈之间彼此之间没有多余的间距差存大。还是上图来看看我们说的瀑布流布局是什么样子。

grid、inline、float 魔性方案

也算是纯 CSS 方案吧,本质上来讲是依赖文档流,从左到右,从上到下。

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

DOM 结构

  1. div.list     // 设置 gird 或者 block,注意清除浮动
  2.   div.item   // 设置为 inline 或者 float,使其能流动
  3.     img      // 设置定宽,高度自适应,间距等。

grid 方案说明

  1. .wrap-waterfall--grid img{vertical-align: top;width: 100px}
  2. .wrap-waterfall--grid .list{
  3.     display: grid;
  4.     grid-gap: 10px;
  5.     /* 可以看到,网格大小,占据位置是需要提前设定的 */
  6.     grid-template-columns: repeat(4, 1fr);
  7.     grid-auto-rows: minmax(50px, auto);
  8. }

grid 在某些情况下会比 flex 好用。比如说需要突破行的限制,但是只适用于固定布局,如下图的布局,如果不使用grid你会如何实现呢?

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

columns、flex CSS实现 不靠谱方案

也是纯 CSS 方案,相比较上面的方案而言,方案已经可以接受,只是还有部分问题。

顺序是先垂直,后水平

(columns)兼容性问题

(flex)需要给一个固定高度,会出现超出设定列,以及无法充满设定列。

columns 方案

天生支持,只需要给父级设置即可 columns: 4; column-gap: 6px; 。

flex 方案

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

absolute、通道 高度计算方案 靠谱方案

这里的方案就靠谱起来了,可以满足我们使用要求。

我们来回忆一下我们的需求:展示一些内容,内容有特性定宽,不定高。不定高一般是因为内容长度或者高度不一致导致的,常见内容又分为两种文字和图片。

文字的话,在没有异步字体的情况下,可以理解为同步就可以获取到盒子高度。

图片的话,因为加载是异步的,所以获取盒子的真实高度也是异步的。但是这里一般分为两种情况

无高度,那么可以通过onload来监听图片加载完成。等图片加载完成再去获取高度。

有高度,这种方案一般用在封面图、或者文章中,在上传图片的时候会保存原图尺寸,这个时候我们就可以直接使用已有数据。

获取图片高度

  1. // 用于获取图片的真实高度
  2. naturalHeight: 1180
  3. // 用于获取图片的真实宽度
  4. naturalWidth: 1200
  5.  
  6. //用户获取图片当前的渲染高度(会受 css 影响)
  7. height: 98
  8. //用户获取图片当前的渲染宽度(会受 css 影响)
  9. width: 100
  10.  
  11. // 可返回浏览器是否已完成对图像的加载。如果加载完成,则返回 true,否则返回 fasle。
  12. complete 属性
  13. // 可以监听到图片加载完成的动作
  14. onload

基于上面的内容,那我们可以先判断 complete 属性,

  1. function getImageSize(img){
  2.     if(img.complete){
  3.         return Promise.resolve({
  4.             naturalHeight: img.naturalHeight,
  5.             naturalWidth: img.naturalWidth,
  6.             height: img.height,
  7.             width: img.width,
  8.         })
  9.     }else{
  10.         return new Promise((resolve, reject)=>{
  11.             img.addEventListener('load', ()=>{
  12.                 resolve({
  13.                     naturalHeight: img.naturalHeight,
  14.                     naturalWidth: img.naturalWidth,
  15.                     height: img.height,
  16.                     width: img.width,
  17.                 })
  18.             })
  19.         })
  20.     }
  21. }
  22. /*
  23. // 测试用例
  24. el = document.createElement('img');
  25. el.src = 'http://cors-www.lilnong.top/favicon.ico?'+Math.random()
  26.  
  27. getImageSize(el).then(console.log).catch(console.error)
  28. setTimeout(()=>getImageSize(el).then(console.log).catch(console.error), 1000)
  29. */

absolute 计算高度方案

因为普通的布局已经无法满足我们的需求,所以我们可以考虑通过 position: absolute 来使内容通过绝对定位来显示

核心操作就是维护每个元素的 left、top,然后使用 left 和 top 去渲染到正确位置。

  1. getListPosition(){
  2.     // 视口宽度 / 每列宽度 得出划分为几列
  3.     let col = this.screenWidth / this.itemWidth >> 0;
  4.     var arr = [];
  5.     for(var i = 0; i < col; i++) arr.push({
  6.         list: [],
  7.         height: 0,
  8.     })
  9.     // 遍历所有元素
  10.     this.listInfo.forEach((item,idx)=>{
  11.         // 找到最低的一列
  12.         var colIndex = 0;
  13.         for(var i = 1; i < col; i++){
  14.             if(arr[colIndex].height > arr[i].height){
  15.                 // colItem = arr[i]
  16.                 colIndex = i
  17.             }
  18.         }
  19.         // 修改元素的信息
  20.         // 所属列
  21.         item.line = colIndex;
  22.         // 计算之后的 top 距离
  23.         item.top = arr[colIndex].height+ 'px';
  24.         // 计算之后的 left 距离
  25.         item.left = colIndex * (this.itemWidth + 10) + 'px'
  26.  
  27.         // 累加操作
  28.         arr[colIndex].list.push(item);
  29.         arr[colIndex].height += item.height + 10;
  30.     })
  31.     return arr
  32. },

通过计算,我们可以到,瀑布流布局下每个元素的位置,通过绝对定位就可以实现。

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

因为上个方案用到了绝对定位,那么有没有不用绝对定位的方案呢?回到我们的问题点上 定宽,不定高,那我们完全可以通过分开渲染放弃 absolute 来实现。

  1. jsGroupList(){
  2.     return this.list.reduce((s,n,idx)=>{
  3.         // 根据下标,直接分配所属列
  4.         s[idx % 4].push({idx: idx, item: n})
  5.         return s
  6.     }, [[],[],[],[],])
  7. },

看开头是实现类似的功能的,但是有一个弊端(快来评论区回复呀)。

通过高度计算,然后分通道,避免 absolute

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

  1. jsGroupHeightList(){
  2.     var list = [
  3.         {height: 0, list: []},{height: 0, list: []},
  4.         {height: 0, list: []},{height: 0, list: []},
  5.     ]
  6.     // 遍历每个元素
  7.     for(var i = 0; i < this.list.length; i++){
  8.         // 当元素有大小的时候在进行操作。
  9.         if(!this.listInfo[i].height) return list;
  10.         // 默认第一个通道是最小高度列
  11.         var minHeightItem = list[0];
  12.         // 计算最小高度列
  13.         list.forEach(v=>{
  14.             if(v.height < minHeightItem.height) minHeightItem = v
  15.         })
  16.         // 把新的元素高度累加到列中。
  17.         minHeightItem.height += this.listInfo[i].height
  18.         // 把新的元素push到列中
  19.         minHeightItem.list.push({idx: i, item: this.list[i]})
  20.     }
  21.     return list;
  22. },

总结

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

方案 优点 缺点 点评
columns 实现简单、纯 CSS 方案 兼容性 -
flex - 需要固定高度,填充难以控制等问题 -
float、inline、bootstrapGrid - - 没点大都用不出这方案
grid - - 可以nth-child模拟实现、或者等待兼容性 masonry
absolute 效果好 - JS计算无限可能
js普通通道 - 填充难以控制 -
js优化通道 效果好、无绝对定位 在出现夸列等操作的时候不是很好控制 -
常见问题
所有VIP可以商用吗,会不会有版权问题?
本站提供的源码大部分可以作为源码开发参考和学习,部分开源可商用。
账号可以分享给其他人使用吗?
您好,账号禁止共享,我们有验证机制的,会自动封号处理,谢谢配合。
如果遇到问题怎么联系解决呢?
最快的途径是在导航菜单选择提交【售后】会在24小时处理
下载的源码可以使用吗?
我们会在显著的地方表明【已测试】,显示已测试的都是小编测试过 。
1. 本站所有资源来源于用户上传和网络,因此不包含技术服务请大家谅解!
2. 本站不保证所提供下载的资源的准确性、安全性和完整性,资源仅供下载学习之用!如有链接无法下载、失效或广告,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
5.如有侵权请联系客服邮件770973008@qq.com
原文链接:https://www.56admin.com/18726.html,转载请注明出处。
0
分享海报

评论0

请先
九年老站,质量有保证,90%亲自测试!优惠原价199模板币终生SVIP,优惠价68模板币! 数量有限! 购买SVIP 所有资源免积分下载,畅享无忧!
显示验证码
没有账号?注册  忘记密码?