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;width:"+this.imgWidth+"px;><p>"+(n+1)+"</p><img src='img/loading.gif' data-src='img/"+(n+1)+".png' style=height:"+(height-45)+"px;width:"+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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM