美文网首页
原生js之瀑布流

原生js之瀑布流

作者: 弦生_a3a3 | 来源:发表于2020-05-23 13:53 被阅读0次

我们已经总结出了瀑布流式布局的两大特征:

内容框宽度固定,高度不固定。

内容框从左到右排列,一行排满后,其余内容框就会按顺序排在短的一列后。

那么我们根据这两大特征就不难想出它的实现原理:

首先我们先通过计算一行能够容纳几列元素(因为我们需要在不同的设备上浏览),然后在通过计算比较找出这一列元素中高度之和最小一列,然后将下一行的第一个元素添加至高度之和最小的这一列的下面,然后继续计算所有列中高度之和最小的那一列,然后继续将新元素添加至高度之和最小的那一列后面,直至所有元素添加完毕。

可能饶了这么一大段话还是有小伙伴不明白,我们还是看图说话,这才是最容易理解的;

(1)首先我们放上一行<div>,它们高度不同,宽度相同:

高度不同,宽度相同

(2)找出所有元素高度之和的最小的那列在那一列的下面添加新的元素

在第五列添加新元素

(3)然后继续计算,获取高度之和最小的那一列,在那一列继续添加新元素

继续在高度之和最小那一列添加新元素

看了这几张图是不是明确了很多,就是这样不停的计算,不停的添加新元素,直至所有元素添加完成。但是我们都知道一些元素是块级元素,一个元素占据一行,这里就会有小伙伴想到了,我们可以利用浮动啊,具体是不是,我们继续实验。

现在明白了瀑布流是布局是怎么实现的了,那么就来写具体的代码来实现一下吧!

四.实现步骤及方法

1.通过浮动的方式进行布局

首先我们添加一些

元素,并设置为左浮动,分别给这些元素给予不同的背景方便我们观察,注意,这些元素的宽度是一样的,只是高度不一样。

通过左浮动设置的布局

这就是通过左浮动设置的布局样式,我们可以看到在第二行的时候,第七个盒子浮动是根据第三个盒子来进行定位的。第三行的时候第七个盒子又挡住了第十个盒子的去路,所以这样我们是没法完成布局的。

2.通过定位的方式进行布局

前面我们已经知道了浮动布局不能满足我们的需求,所以我们寻求另外一种布局方式,那就是通过定位,给每个盒子设置定位属性后,我们就可以通过动态的设置相应的top,left值来让盒子规规矩矩的为我们排列。

(一)给所有盒子的父元素加上相对定位属性,给所有盒子加上绝对定位属性:

position:relative;

position:absolute;

(二)定位之后我们得到的界面是这样的:

设置定位后的元素

(三)如何判断一行有多少列:

因为我们的界面宽度是变化的,所以列数也需要跟着变化,动态设置列数的原理大致如下:

1.获取到页面的宽度

2.获取到每个盒子的宽度

3.需要显示的列数 = 页面宽度/盒子宽度

注意:一般盒子之间都有一个间隙,所以我们的公式变成如下:

显示的列数(column) = 页面宽度(pageWidth)/(盒子宽度(itemWidth)+间隙(gap))

(四)排列第一行

我们有很多盒子,太多了我们就不好管理,所以我们把所有盒子装进一个数组arr里面,下标从0开始,用i来代表盒子。那么我们如何知道这个盒子是不是应该排列在第一行呢。其实很简单,想一下,我们这么多盒子,全部都要动态布局,动态的设置他的left、top值,那么for循环肯定是避免不了的,当i(所有盒子的索引)<column(显示的列数)的时候,盒子应该在第一行。比如说我们一行有5个盒子,那么第五个盒子的索引就是4,由于4<5,所以在第一行,如果索引为6,由于6>5,所以在第二行。代码基本就是这个样子

if(i<columns){//确定第一行items[i].style.top=0;items[i].style.left=(itemWidth+gap)*i+'px';}

知道了盒子在第1行之后,我们只需要动态的设置盒子的left值就能排列好了。

怎么动态计算每个盒子的left值呢:left=i*(itemWidth+gap),比如说我们排列第二个盒子,它的索引值i就是1,i*(itemWidth+gap)刚好就是第一个盒子和间隙的宽度和,所以距离left值也刚刚好。

说了这么多,画个图理解一下:

获取left值

(五)获取所有列的高度和

在第一行排列好后,我们就需要排列第二行了,但是第二行我们需要用到top值了,前面原理的时候说了,我们就是根据每一列的最小高度和来进行排列,所以这里我们需要计算出第一行所有列的高度和并保存。

我们定义一个数组arr来保存高度。

我们需要在页面一进入的时候就获取高度,也就是写在onload里面,因为图片的加载特征是:等页面都加载完之后才去请求加载,所以不写在入口函数里可能会出现高度获取不到的情况。

(六)排列第二行

获取到刚刚数组中,高度最小的那一列,将第2行的第1个盒子放置在它的下方;

此时的left值就是高度最小列的offsetLeft;top值就是第1行高度最小列的高度(为了布局美观可以加上上下间隙gap)。

记录下高度最小列的索引index,后面计算会用到;

设置完成之后,会发现后面所有的盒子都叠在这个高度最小列的下面,原因就是此时的最小列高度没有改变,应该加上下面盒子 的高度,得出一个新高度。我们需要在最小列后面加了一个盒子之后重新计算所有列的最小高度的列。

看图说话:我们看到第六个盒子下面下面全是其他盒子。

将第二行第一个盒子排列在最小高度和的那一列下面

(七)重新获取最小高度列的高度

排了第二行的第一个盒子之后,最小高度列已经发生了变化,我们需要重新计算一下

当前高度最小列的高度 = 当前高度最小列的高度 + 间隙 + 下面图片盒子的高度

看图说话:现在知道我们保留index做什么了吧。

(八)设置为响应式

将整个设置样式的部分封装成一个函数,在onload里面注册一个resize事件,只要页面一发生改变,就触发样式部分的代码。

实时改变pageWidth的宽度,这样瀑布流就会是一个响应式的效果了

(九)添加懒加载效果

懒加载的实现原理可以参考我的上一篇文章,说得很清楚详细。大致原理用一张图来描述:

懒加载实现原理图

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>瀑布流</title>

    <style>

        * {

            margin: 0;

            padding: 0;

        }

        .item {

            float: left;

            transition: .3s ease;

            display: flex;

            justify-content: center;

            align-items: center;

            font-size: 30px;

            font-weight: 700;

            color: aliceblue;

            margin-right: 15px;

            margin-bottom: 15px;

            width: 205px;

            position: absolute;

        }

        .item-1 {

            background-color: rgb(206, 169, 169);

            height: 300px;

        }.item-2 {

            background-color: rgb(131, 226, 174);

            height: 150px;

        }.item-3 {

            background-color: rgb(77, 30, 30);

            height: 350px;

        }.item-4 {

            background-color: rgb(49, 62, 134);

            height: 300px;

        }.item-5 {

            background-color: rgb(230, 99, 99);

            height: 200px;

        }.item-6 {

            background-color: rgb(206, 169, 169);

            height: 300px;

        }.item-7 {

            background-color: rgb(124, 126, 145);

            height: 400px;

        }.item-8 {

            background-color: rgb(169, 199, 38);

            height: 230px;

        }.item-9 {

            background-color: rgb(114, 128, 53);

            height: 300px;

        }.item-10 {

            background-color: rgb(48, 54, 18);

            height: 260px;

        }.item-11 {

            background-color: rgb(118, 122, 96);

            height: 230px;

        }.item-12 {

            background-color: rgb(118, 122, 96);

            height: 240px;

        }.item-13 {

            background-color: rgb(118, 122, 96);

            height: 250px;

        }.item-14 {

            background-color: rgb(118, 122, 96);

            height: 270px;

        }.item-15 {

            background-color: rgb(118, 122, 96);

            height: 330px;

        }.item-16 {

            background-color: rgb(118, 122, 96);

            height: 200px;

        }.item-17 {

            background-color: rgb(118, 122, 96);

            height: 100px;

        }.item-18 {

            background-color: rgb(118, 122, 96);

            height: 400px;

        }.item-19 {

            background-color: rgb(118, 122, 96);

            height: 340px;

        }.item-20 {

            background-color: rgb(118, 122, 96);

            height: 350px;

        }.item-21 {

            background-color: rgb(118, 122, 96);

            height: 360px;

        }.item-22 {

            background-color: rgb(118, 122, 96);

            height: 370px;

        }

    </style>

</head>

<body>

    <div id="box">

        <div class="item item-1">1</div>

        <div class="item item-2">2</div>

        <div class="item item-3">3</div>

        <div class="item item-4">4</div>

        <div class="item item-5">5</div>

        <div class="item item-6">6</div>

        <div class="item item-7">7</div>

        <div class="item item-8">8</div>

        <div class="item item-9">9</div>

        <div class="item item-10">10</div>

        <div class="item item-11">11</div>

        <div class="item item-12">12</div>

        <div class="item item-13">13</div>

        <div class="item item-14">14</div>

        <div class="item item-15">15</div>

        <div class="item item-16">16</div>

        <div class="item item-17">17</div>

        <div class="item item-18">18</div>

        <div class="item item-19">19</div>

        <div class="item item-20">20</div>

        <div class="item item-21">21</div>

        <div class="item item-22">22</div>        

    </div>

</body>

<script>

    var items = document.getElementsByClassName('item');

    //定义间隙10像素

    var gap = 10;

    //进页面执行函数

    window.onload = function () {

        waterFall();

    }

    function waterFall() {

        //首先确定列数 = 页面的宽度 / 图片的宽度

        var pageWidth = getClient().width;

        var itemWidth = items[0].offsetWidth;

        var columns = parseInt(pageWidth / (itemWidth + gap));

        var arr = [];//定义一个数组,用来存储元素的高度

        for(var i = 0;i < items.length; i++){

            if(i < columns) {

                //满足这个条件则说明在第一行,文章里面有提到

                items[i].style.top = 0;

                items[i].style.left = (itemWidth + gap) * i + 'px';

                arr.push(items[i].offsetHeight);

            }else {

                //其他行,先找出最小高度列,和索引

                //假设最小高度是第一个元素

                var minHeight = arr[0];

                var index = 0;

                for(var j = 0; j < arr.length; j++){//找出最小高度

                   if(minHeight > arr[j]){

                       minHeight = arr[j];

                       index = j;

                   } 

                }

                //设置下一行的第一个盒子的位置

                //top值就是最小列的高度+gap

                items[i].style.top = arr[index] + gap + 'px';

                items[i].style.left = items[index].offsetLeft + 'px';

                //修改最小列的高度

                //最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 间隙的高度

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

            }

        }

    }

    //当页面尺寸发生变化时,触发函数,实现响应式

    window.onresize = function () {

        waterFall();

    }

    // clientWidth 处理兼容性

    function getClient() {

        return {

            width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,

            height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight

        }

    }

    // scrollTop兼容性处理

    function getScrollTop() {

        return window.pageYOffset || document.documentElement.scrollTop;

    }

</script>

</html>

——————————————————转载于https://zhuanlan.zhihu.com/p/55575862

相关文章

  • 原生js之瀑布流

    我们已经总结出了瀑布流式布局的两大特征: 内容框宽度固定,高度不固定。 内容框从左到右排列,一行排满后,其余内容框...

  • 原生js实现瀑布流

    浏览网页的时候经常会遇到瀑布流布局的网站。也许有些读者不了解瀑布流。瀑布流,又称瀑布流式布局。是比较流行的一种网...

  • 原生js瀑布流代码

  • Js实现瀑布流

    1.使用原生js实现瀑布流效果,demo地址(https://xukeler.github.io/waterfal...

  • 原生js实现瀑布流效果

    实现目标:实现瀑布流布局、当滚动条滚动到一定距离时加载图片瀑布流布局:结合窗口改变 定位第一行的盒子 第二行第一...

  • 原生js写的瀑布流

    这里只写了js和css方法,大家有兴趣可以动手写写

  • 原生Js实现瀑布流效果

    盗用的效果图 效果分析 瀑布流中图片的宽度都是固定的,但是高度需要设置为auto以使图片不变形,高度不固定。首先想...

  • 原生js的瀑布流写法

    以下是一种原生js的瀑布流写法,或许具体实现过程需要慢慢尝试,但是这种思想很重要。就是对每张图片执行一个循环,在每...

  • 瀑布流布局

    瀑布流布局http://js.jirengu.com/dunawupili/1/ 2.新闻瀑布流http://js...

  • 原生js实现瀑布流效果(转载)

    写的特别好 文章链接

网友评论

      本文标题:原生js之瀑布流

      本文链接:https://www.haomeiwen.com/subject/tmgkahtx.html