最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 瀑布流的原理以及实现

    正文概述 掘金(B_Cornelius)   2021-08-09   430

    如何实现瀑布流

    在最近的业务中,需要实现以下功能 :图片呈现两列排行,顺序从上到下,从左到右。具体如下:

    瀑布流的原理以及实现

    懒人最先想到的使用市面上现有的库,在网上找到react-masonry-component这个库用于实现瀑布流。实际使用后发现效果有些差,为什么呢?因为可以明显数据加载完后,所有的图片都堆叠在一起了,然后监听到图片的onload事件以后,页面开始重新排版,就有个闪动的过程。产品表示无法接受这种情况,并且在自己业务中我们其实是事先知道这个图片的宽高的。所以在本业务中完全可以预设这个图片的宽高,避免这种不断重新排版的情况。因为现有的库不能满足我们的需求,所以干脆自己动手实现这个瀑布流

    原理

    在移动端的大多数情况下,图片都是等宽不等高。如下图,我们在第一排排下了多个等宽不登高的元素

    瀑布流的原理以及实现

    新元素需要放置第一行元素最短的一个后面

    瀑布流的原理以及实现

    如果还需要在添加元素,需要计算出所有列中最短的列,然后把元素放到最短的列后面

    效果大概就是如下,现在来具体分析一下其中的步骤

    首先我们需要明确所有的内容其实通过脱离文档流,用定位把元素定位在各自的位置上,所以先调整元素的样式

    <div className="container">
        {
            list.map(item => (<img src={item.src} className="item" />))
        }
    </div>
    <style>
    .container {
    
        position: relative;
    }
    .item {
        position: absolute
    }
    </style>
    

    排列第一行

    现在我们假设一共需要排列column列,然后每列之间的间隔是gap宽度,每一列的宽度为columnWidth。需要渲染的列表是list,每个元素都需要设置他的left值,和top值。因为第一行没有前置元素,所以这里单独处理一下。

    const items = docuquery.querySelectorAll('item');
    items.forEach((item, index) => {
        if (index < column) {
            item.style.top = 0;
            item.style.left = (columnWidth + gap) * index
        }
    })
    

    从上面可以看到元素的left的值就是每列的宽度加上边距,在乘上当前是属于第几列。

    获取所有列的最小高度

    在对第一行进行排版以后,开始最二行进行排版,新加入的元素需要在第一行最短的元素的后面,所以我们需要计算所有列的高度进行保存,然后得出高度最小列的长度和最小列所在的索引,所以在第一行元素的排版代码中,我们对每列的高度进行存储

    const items = docuquery.querySelectorAll('item');
    items.forEach((item, index) => {
        if (index < column) {
            item.style.top = 0;
            item.style.left = (columnWidth + gap) * index
            arr.push(item.style.top + top)
        }
    })
    

    瀑布流的原理以及实现

    例如上图,元素6的top值就是元素3的高度加上元素3的高度。他的left值就是最小列元素的offsetLeft。代码表示如下:

       let minHeight = arr[0];
       let index = 0;
       for (let j = 0; j < arr.length; j++) {
          if (minHeight > arr[j]) {
            minHeight = arr[j];
            index = j;
          }
      }
    

    排列剩余元素

    我们假设第一行第一个元素是最小的,遍历高度的存储数组,找到高度最小的那个,并且找到他的索引列值,然后开始设置下一个元素的位置

    items[i].style.top = `${arr[index] + gap}px`;
    items[i].style.left = `${items[index].offsetLeft}px`;
    

    设置完新元素的位置后,我们需要更新最小列的高度信息,具体如下:

     arr[index] = arr[index] + items[i].offsetHeight + gap;
    

    当所以结果都计算完以后,我们需要更新父元素的高度。因为在文档开头,我们使用了绝对定位,让元素脱离了文档流

     const container = document.querySelector('.container')
     container.style.height = `${max(arr)}px`;
    

    以上就是瀑布流的实现,也是网上大部分教程的内容

    重排列

    但是在实际项目中,我们还需要考虑这个图片的加载过程,因为不定高的图片没有加载完成的时候是拿不到他的高度的。所以我们还需要监听这个图片的加载过程,每次当图片加载完成以后我们都需要对其进行重新排版,所以需要在上述代码中加入:

    const images = container.querySelectorAll('img');
          images.forEach(image => {
            image.onload = () => {
              refresh();
            };
          });
    

    获取到容器中会影响布局的图片,然后获取他们,监听他们的onload事件,当某个图片加载完成以后,需要重新排列。

    此外考虑到DOM结构的动态修改,所以借助MutationObserver实现对DOM结构变化的监听

    const observe = new MutationObserver(() => {
          refresh(options);
        });
    

    使用flex实现瀑布流

    除了使用固定定位外,我们也可以使用flex来实现瀑布流。例如在前面例子中,一共有column列,列与列之间的宽度为gap。

    原理

    在flex中,我们可以在容器下面放置column个盒子,然后真正需要排列的元素,就放到每个盒子内部。

    瀑布流的原理以及实现 下面是DOM的结构

    <div className="contianer">
        <div className="flex1">
            {
                list1.map((item) => <img src={item.src}>)
            }
        </div>
        <div classsName="flex2">
             {list2.map((item) => <img src={item.src}>)}
        </div>
        <div classsName="flex3">
             { list3.map((item) => <img src={item.src}>)}
        </div>
    </div>
    <style>
        .containter {
            display: flex;
        }
    </style>
    

    在容器下面我们维护了column个列表,在子盒子下面渲染对应的列表,例如第一个盒子就渲染第一个列表,以此类推。大多数市面上的文章就介绍到这里就戛然而止,但是事实上我们需要对提供的列表进行重新组装,保证排列的先后顺序

    首先还是单独对第一行进行处理,首先生成column长度的数组去存储每列需要渲染的元素

    const arr = Arry.from({length: column}).fill([])
    list.forEach((item, index) => {
        if (index < column) {
            arr[0].push(item)
        }
    })
    

    然后在添加第二行的时候我们仍然需要去获取当前高度最短的列,然后把元素加入对应的列数中。至于最小高度计算和最小高度的列的计算方式和上面绝对定位的方式类似,这里就不一一讲解了

    相比较于绝对定位,在最后我们不需要设置容器的高度,因为容器的高度能够被容器内部的元素撑起来的。

    总结

    对比两种实现方式,核心的内容都是一样的,需要明白如何去计算最小的高度,新加入的元素如何加入到已经的排列结果中的。如果图片的宽高是未知的,还需要去监听图片的宽高进行重新排版。


    起源地下载网 » 瀑布流的原理以及实现

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元