WEB前端——JS实现瀑布流

一、简介

1、什么是瀑布流?
瀑布流式布局:一种较为流行的页面布局,视觉表现为参差不齐的多栏布局。随着滚动条向下滚动,不断加载数据块并附加至当前尾部。
特点:整版以图片为主,大小不一的图片按照一定的规律排列。

二、实现效果

1、图片分四列展示
2、向下滚动,自动加载数据并渲染显示。

三、具体实现

1、架构:用ul-li列表结构,每个li中插入div作为图片单元。
2、样式:重点是掌握浮动流
3、功能:
Ajax获取数据->success函数调用renderDom函数
->滚动滑轮重新获取数据

代码如下:

瀑布流代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            list-style:none;
            padding:0;
            margin:0;
        }
        .wrapper{
            border:1px solid black;
            width:960px;
            margin:0 auto;
            background-color: #eee;
        }
        .wrapper::after{
            content:"";
            display:block;
            clear:both;
        }
        .item{
            float:left;
            width:25%;
        }
        .item div{
            background-color:#fff;
            margin:10px;
            padding:10px;
        }
        .item img{
            width:100%;
        }
    </style>
</head>
<body>
    <ul class="wrapper">
        <li class="item">
            <!-- <div>
                <img src="./image/1.jpg" alt="第一张照片">
                <p></p>
            </div> -->
        </li>
        <li class="item">
            <!-- <div>
                <img src="./image/2.jpg" alt="第二张照片">
                <p></p>
            </div> -->
        </li>
        <li class="item">
            <!-- <div>
                <img src="./image/3.jpg" alt="第三张">
                <p></p>
            </div> -->
        </li>
        <li class="item">
            <!-- <div>
                <img src="./image/4.jpg" alt="第四张">
                <p></p>
            </div> -->
        </li>
    </ul>


    <script src="./ajax.js"></script>
    <script>
        //从第71行到第120行可以显示,但是没有按照想要的顺序排列。原因是由于图片加载需要时间,(异步读取数据)
        //比如,第一张图片还没加载完,就在判断第五个图片需要加在何处(此时可以认为第一列为0)那么就造成了第五张图片在第一列加载;
        //解决方案:预留图片的高度 判断 offsetHeight和pageYoffset+window.innerHeight的大小判断是否空白。
        function getData(){
            ajax({
            url:"./data.json",
            method:"GET",
            success:function(data){
                console.log(data);
                renderDom(data);
                lock=false;
            }   
        });
        }
        getData();
        //图片真实的宽度
        var imgWidth=document.querySelector('.item').offsetWidth-40;
        //渲染dom结构
        function renderDom(data){
            // for(var i=0;i<data.length;i++)
            var oLi=document.getElementsByClassName('item');
            data.forEach(function(item){//每一个item都需要加入dom 
                //查找最短列,并且向最短列里添加Dom结构

                //最短列的索引值
                var index=getMinLi().minIndex;
                console.log(index);
                //创建dom
                var dom=createImg(item);
                //将dom加入
                oLi[index].appendChild(dom);
            })
        }

        //查找最短列
        function getMinLi(){
            //获取到最短的li
            var items=document.getElementsByClassName('item');
            //minIndex代表最短列的索引值
            var minIndex=0;
            var minHeight=items[0].offsetHeight;
            for(var i=0;i<items.length;i++){
                if(minHeight>items[i].offsetHeight){
                    minIndex=i;
                    minHeight=items[i].offsetHeight;
                }             
            }
            return {
                minIndex,
                minHeight
            }
        }
        function createImg(data){

            // <!-- <div>
            //     <img src="./image/4.jpg" alt="第四张">
            //     <p></p>
            // </div> -->

            var oDiv=document.createElement('div');
            var img=new Image();
            img.src=data.img;
            //在保留图片原来的宽高比的情况下预留img的高度
            //图片的原始宽高为data.width / data.height=img.width/img.height
            img.height=imgWidth*data.height/data.width;
            var oP=document.createElement('p');
            oP.innerText=data.desc;
            oDiv.appendChild(img);
            oDiv.appendChild(oP);
            return oDiv;
        }



        //lock代表当前是否正在网络请求数据过程当中
        // var lock=false;
        //延迟触发网络请求
        var timer=null;
        //监听滚动事件
        window.onscroll=function(){
            //判断当前页面是否出现空白区域
            //如果出现空白区域,则需要重新获取数据,渲染页面

            clearTimeout(timer);
            //判断最短列同滚动距离和可视区之和
            if(getMinLi().minHeight<window.pageYOffset+window.innerHeight){
            //     //性能优化
            //     //1、当当前有网络请求的时候不予发送数据
            //     if(!lock){
            //     lock=true;//保证getData()互斥访问,同步与互斥
            //     getData();
            //     }

                //2、在不断的滚动滚动条的时候,不进行网络请求,只有停止滚动才会进行请求
           timer=setTimeout(function(){
                    getData();
                },500);
                  
        }
    </script>
</body>
</html>

自制json数据:

//json 文件
[
    {
        "img":"image/1.jpg",
        "desc":"第1张图片",
        "height":1138,
        "width":640
    },
    {
        "img":"image/2.jpg",
        "desc":"第2张图片",
        "height":1080,
        "width":1920
    },
    {
        "img":"image/3.jpg",
        "desc":"第3张图片",
        "height":640,
        "width":400
    },
    {
        "img":"image/4.jpg",
        "desc":"第4张图片",
        "height":1381,
        "width":640
    },
    {
        "img":"image/5.jpg",
        "desc":"第5张图片",
        "height":681,
        "width":400
    },
    {
        "img":"image/6.jpg",
        "desc":"第6张图片",
        "height":1067,
        "width":1919
    },
    {
        "img":"image/7.jpg",
        "desc":"第7张图片",
        "height":889,
        "width":500
    },
    {
        "img":"image/8.jpg",
        "desc":"第8张图片",
        "height":1139,
        "width":640
    },
    {
        "img":"image/9.jpg",
        "desc":"第9张图片",
        "height":795,
        "width":460
    },
    {
        "img":"image/10.jpg",
        "desc":"第10张图片",
        "height":1080,
        "width":1920
    },
    {
        "img":"image/11.jpg",
        "desc":"第11张图片",
        "height":1920,
        "width":1080
    },
    {
        "img":"image/12.jpg",
        "desc":"第12张图片",
        "height":1871,
        "width":1080
        },
    {
        "img":"image/13.jpg",
        "desc":"第13张图片",
        "height":429,
        "width":650
    },
    {
        "img":"image/14.jpg",
        "desc":"第14张图片",
        "height":1080,
        "width":1920
    },
    {
        "img":"image/15.jpg",
        "desc":"第15张图片",
        "height":450,
        "width":500
    },
    {
        "img":"image/16.jpg",
        "desc":"第16张图片",
        "height":1070,
        "width":602
    },
    {
        "img":"image/17.jpg",
        "desc":"第17张图片",
        "height":750,
        "width":468
    },
    {
        "img":"image/18.jpg",
        "desc":"第18张图片",
        "height":1280,
        "width":800
    },
    {
        "img":"image/19.jpg",
        "desc":"第19张图片",
        "height":1846,
        "width":1080
    },
    {
        "img":"image/20.jpg",
        "desc":"第20张图片",
        "height":1215,
        "width":700
    }
]

ajax封装:

/**
 * @param {Object} options
 * @desc 封装一个ajax函数,使所有网络请求都用该函数。
 *      参数:
                1.method:请求方式
                2.url:请求地址
                3.data:请求参数
                4.isAsync:是否异步
                5.success:成功拿到数据后做的功能
                6.error:失败的回调函数。
            当前函数有局限性:只能为字符串
            method只能为get和post
 **/
function ajax(options){
    var method=options.method||"GET";
    var url=options.url||"";
    var data=options.data||"";
    var isAsync=options.isAsync==undefined?true:toptions.isAsync;
    var success=options.success||function(){};
    var error=options.error||function(){};

    var xhr=null;
    if(window.XMLHttpRequest){
        xhr=new XMLHttpRequest();
    }else if(window.ActiveXObject){
        xhr=new ActiveXObject("Microsoft.XMLHTTP");
    }else{
        return alert("该浏览器不支持XMLHttpRequest");
    }


    xhr.onreadystatechange=function(){
        if(xhr.readyState==4){
             if(xhr.status==200){
                //接收到数据之后的处理
                success(JSON.parse(xhr.responseText));
            }
        }
    }  
    xhr.onerror=function(e){
        error("error");
    }
    //同一将用户传过来的参数方式转换为大写。
    //  method=method.toUpperCase(); 
    if(method=="GET"){
        if(url.indexOf('?')>-1){//判断url中是否有问号
            //判断问号的位置是否在最后,如果是则直接拼接,否则证明有数据 
            if(url.indexOf('?')===url.length-1){
                url+=data;
            }else{
                url+='&'+data;
            }
        }else{
            url+='?'+data;
        }
        xhr.open(method,url,isAsync);
        xhr.send();
    }
}