zoukankan      html  css  js  c++  java
  • js实现响应式瀑布流

      导读:瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格,像花瓣网、蘑菇街、美丽说等。

      改进版的代码见:github,可以与现有的进行对比,见文章末尾。

      最近在好多地方看到瀑布流的字眼,感觉真的很不错,于是就想自己能不能写一个呢,而且是响应式的。经过将近两天的研究,终于写出来了,先传几张图给大家看看最终的效果:


                      

      随着浏览器页面的大小调整。布局从四列逐渐变成三列两列,甚至是一列(图像的块的宽度是保持不变的)。

      在我看来,在复杂的程序其实都是由很简单的函数组合在一起的,另外有一些经常用到的部分也会被抽取出来当做一个新的函数。然后,为了封装,会将一些变量也封装起来,然后通过外部传递进来,最后就成了我们现在看到的。其实一开始,很少人能够确定要怎么写,且能确定需要传进哪些参数。一开始只能大概确定需要做哪些,需要哪些函数,写着写着,发现缺了在添加就好了。至少我自己是这样的(也许是我现在太渣了)。

      好了,接下来说一说 具体实现的过程。请结合源码来看。

      第一步:我们需要创建这些图像块,也就是源码中的createDiv函数。这里为了节省性能,我们采用字符串的形式,而不用createElement等这些函数。然后这里你会发现一个问题,我们生成的块的大小是一样的。这时候,我们就需要用到random函数了。通过该函数来生成高度不一样的图像块。

      第二步:怎样确定图像的列数和怎样让他随着页面的改变而改变自己的列数。这里我们就得用到widthchange函数。首先图像块的父容器的外边距是20px;因此总的宽度就得减去40(这里以浏览器页面来算)。然后再根据图像块的宽度,来确定每一列之间的列数和间距。

      第三步:设置样式setStyle。图像块的大小确定之后,我们得设置图像块的样式,比如大小,间距等等。

      第四步:把上面的结合起来,进行封装就好了。

    -------------------------------------------------------------------------------------------------------------------

      但是呢,我们得考虑一个问题,就是图片太多了,我们不可能一次让所有图片都下载,这样会影响网页的性能和用户的体验。那么有没有什么好的办法呢?我们只让可视区的图片显示就好了。那怎么实现呢,我们来分析下

      1、这里涉及到上面的createDiv函数,得先说清楚。我们得将img的src的地址设为其他地址或为空。然后再自定义一个data-src属性,属性值为图片的地址。

      2、可视区的图片显示。也就是我们得先判断图片的位置是不是在这个可视区内,这里就得用到pageY,show函数,这些函数会用到offsetTop,scrollTop和clientHeight等这些属性。

      3、如果在的话,就将data-src的值赋给src,并且将其从数组中去掉,表示该图片已经被加载。

      4、最后将函数进行修改,封装。再绑定好各个事件就大功告成了。

      最后附上源码供大家研究,如果觉得好的话,可以加我的个人微信公众号,这样你就不用访问博客就可以获取相关信息。

      公众号名字:JavaScript学习与实践

      微信号:learnpracticeJs

      公众号二维码:

    欢迎大家关注我的公众号,你的关注就是我最大的支持。

    源代码:

    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>瀑布流 by huansky</title>
        <style>
            #container{
                position: relative;
                top:20px;
                margin: 20px 20px;
                background: #c00;
            }
            #container .wf{
                position: absolute;
                border: 1px solid #ccc;
                font-size: 40px;
                border-radius: 10px;
                overflow: hidden;
            }
            p{
                margin: 0px;
                padding: 0px;
            }
            h1{
                text-align: center;
            }
        </style>
    </head>
    <body>
        <h1>瀑布流 by huansky</h1>
        <div id="container">54454</div>
    </body>
    <script >
        function wf(obj){
            this.colHeight=[];         //每一列的高
            this.colLeft=[];          //每一列的位置; 因为列是等宽的,所以每一列的位置也是固定的。
            this.flag=0;              //标志位,包含图片的容器只需要创建一次
            this.imgWidth=obj.wth;    //设置每一列的宽度
            this.id=document.getElementById(obj.id);  //获取最外层的容器
            this.classname=document.getElementsByClassName(obj.classname);  //获取瀑布流的类名
            //变量一定要在init()之前给定义,不能放在init()之后
            this.init();            //初始化
        }
        wf.prototype={
            //获取m到n的随机值
            random:function(m,n){
            return Math.ceil(Math.random()*(n-m)+m);
            },
    
            //最小值的下标
            getMinCol:function(arr){
                var ca = arr,cl = arr.length,temp = ca[0],minc = 0;
                for(var ci = 0; ci < cl; ci++){
                    if(temp > ca[ci]){
                        temp = ca[ci];
                        minc = ci;
                    }
                }
                return minc;
            },
    
            //获取页面的大小,从而调整列数。
            widthchange:function(){
            
                winWidth = document.body.clientWidth; //获取页面的宽度
                //console.log("ttt   "+document.documentElement.clientHeight);
                var cols=Math.floor((winWidth-40)/this.imgWidth);  //40是页面的两边的边距
                //console.log(this.imgWidth);
                var colsmar=Math.floor((winWidth-40-20*(cols+1))/this.imgWidth); //列数
                //console.log(colsmar);
                var cmargin=Math.floor(winWidth-this.imgWidth*colsmar-40)/(colsmar+1);  //每一列的间距    
                //console.log(cmargin);
                for(var i=0;i<colsmar;i++){
                    this.colHeight[i]=20;
                    this.colLeft[i]=(i+1)*cmargin+i*this.imgWidth;
                }
            },
            //创建页面视图
            createDiv:function(m){
                var div="";
                for(var n=0; n<m;n++){
                    var height=this.random(150,350);
                    div+="<div  class='wf' style=height:"+height+"px;"+this.imgWidth+"px;><p>"+(n+1)+"</p><img src='img/loading.gif' data-src='img/"+(n+1)+".png' style=height:"+(height-45)+"px;"+this.imgWidth+"px;></div>";
                }
                this.id.innerHTML=div;
            },
            //设置每个小块的样式,边距等等
            setStyle:function(){
                for(var i=0;i<50;i++){
                    var lowcol=this.getMinCol(this.colHeight);
                    this.classname[i].style.left=this.colLeft[lowcol]+"px";
                    this.classname[i].style.top=this.colHeight[lowcol]+"px";
                    this.colHeight[lowcol]=this.classname[i].offsetHeight+this.classname[i].offsetTop+20;//offsetTop是数值
                }
            },
            init:function(){
                if(!this.flag){
                //只创建小块一次,不然会每次都得重新加载图片
                    this.createDiv(50);
                    this.flag=1;//设置flag=1,这样下次就不会加载了
                }
                this.widthchange();
                //newContainer.init();
                
                this.setStyle();
    
            }
        }
        
        
        //只加载在可视区内的图片
           function LazyLoad(id){
            this.container=document.getElementById(id);  //获取id
            this.imgs=this.getImgs();                    //得到img数组
            this.init();
        }
    
        LazyLoad.prototype={
    
            init:function(){
                this.update();
            },
            getImgs:function(){
                var arr=[];
                var imgs=this.container.getElementsByTagName("img");
                for (var i=0,len=imgs.length;i<len;i++){
                    arr.push(imgs[i]);
                }
                return arr;
            },
            update:function(){
                if(!this.imgs.length){
                    //console.log(this.imgs.length);
                    return;
                }
                var i=this.imgs.length;
                for(--i;i>=0;i--){
                    if (this.show(i)){
                        this.imgs[i].src=this.imgs[i].dataset.src;
                        this.imgs.splice(i,1);
                        //console.log(newContainer.imgs[i].dataset.src);
                    }
                }
            },
            //显示图片,判定图片是否在可是区域内。
            show:function(i){
                var img=this.imgs[i],
                    scrollTop=document.documentElement.scrollTop||document.body.scrollTop,
                    scrollBottom=scrollTop+document.documentElement.clientHeight,
                    imgTop=this.pageY(img),
                    imgBottom=imgTop+img.offsetHeight;
                //如果满足,让他显示。
                if(imgBottom>scrollTop && imgBottom<scrollBottom || (imgTop>scrollTop && imgTop<imgBottom))
                    return true;
                return false;
            },
            //获取图片的最高点的y坐标
            pageY:function(element){
                if(element.offsetParent){
                    return element.offsetTop+this.pageY(element.offsetParent);
                }else{
                    return element.offsetTop;
                }
            }    
        }
    
    
        //var dom=document.getElementById("container");
        //var cName=document.getElementsByClassName("wf");
    
        //绑定事件函数
        function on(element,eventName,listener){
            if (element.addEventListener){
                element.addEventListener(eventName,listener,false);
            }
            else if (element.attachEvent){
                element.attachEvent('on'+eventName,listener);
            }
            else
                element['on'+eventName]=listener;
        }
    
        var newWf=new wf({wth:300,id:"container",classname:"wf"});//创建瀑布流
        var newContainer=new LazyLoad("container"); //惰性加载函数
    
        on(window,"resize",function(){
             newWf.init();
             newContainer.update();
        })
    
        //绑定scroll事件
        on(window,"scroll",function(){
            newContainer.update();
        })
        
    </script>
    </html>
    View Code
  • 相关阅读:
    C# 开发Chrome内核浏览器(WebKit.net)
    SQL 存储过程语句拼接愁人的时间类型
    Linux简介
    揭秘Node.js深受欢迎的原因
    6个强大的AngularJS扩展应用
    ElasticSearch NEST
    web中的各种打印方案
    配置问题总结
    leetcode -day29 Binary Tree Inorder Traversal &amp; Restore IP Addresses
    HBase高速导入数据--BulkLoad
  • 原文地址:https://www.cnblogs.com/huansky/p/5470879.html
Copyright © 2011-2022 走看看