具有分散效果的瀑布流网页

具有分散效果的瀑布流网页

这几天在慕课网学习瀑布流,然后学完之后的大作业就是搞这个,“具有分散效果的瀑布流网页”,嗯,没有参考,也没有标准答案,于是我就开始自己来来尝试一下怎么做。

首先,在网上找了一下相关的结果,看看有什么可以参考的,一百度,就只找到一篇博客谈到这个效果的,就是下面这篇:带有分散效果的瀑布流(jQuery实现)这篇文章就是讲和我一样的网页效果的。由于博主没有贴动图,我也没有想着去完全依照博主的方法来做,所以我没有运行他的代码(也不知道他的效果具体怎样的),只是粗略的看了一下,了解一下大概的思路。博主的在原有的瀑布流函数的基础上,增加了设置图片初始位置的函数,让图片开始的时候可以分散的摆放。这个想法我觉得很好,所以我就决定按照他的这个思路走下去。然后想了一下自己想要的效果,然后就开始一边写一遍构思代码。

首先是html代码:
split-waterfall.html(比较懒所以css样式就没有分开写了。。。。)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script type="text/javascript" src="jQuery-method/js/jquery-1.8.3.min.js"></script>
    <title>带有分散效果的瀑布流</title>
    <style type="text/css">
        *{
            padding: 0;
            margin: 0;
        }
        #main{
            position: relative;
        }
        .pinF{
            padding:15px 0 0 15px;
            float: left;
        }

        .box{
            padding: 10px;
            border:1px solid #cccccc;
            -webkit-box-shadow: 0 0 6px  #ccc;
            -moz-box-shadow: 0 0 6px  #ccc;
            box-shadow: 0 0 6px  #ccc;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            border-radius: 5px;
        }
        .box img{
            width: 162px;
            height: auto;
        }
    </style>
    <script type="text/javascript" src="js3.js"></script>
</head>
<body>
<div id="main">
    <div class="pinA pinF">
        <div class="box"><img src="images/1.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/2.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/3.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/4.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/5.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/6.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/7.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/8.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/21.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/9.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/10.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/11.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/12.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/13.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/14.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/15.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/16.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/17.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/18.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/19.jpg" alt=""></div>
    </div>
    <div class="pinA pinF">
        <div class="box"><img src="images/20.jpg" alt=""></div>
    </div>
</div>

</body>
</html>

这样,页面结构和样式就写完了,接下来是js代码。

经过思考我觉得效果应该是,图片不断加载在窗口的下部的中央的区域里,错乱的摆放好,然后随页面的滚动,图片不断摆放到瀑布流的正确位置,而且图片生成的区域也一直固定在窗口下部中央。

这样的话,就基本需要三个操作函数了,首先第一个是瀑布流的函数,然后是遍历图片的函数,最后是图片具体位置设置的函数。这三个函数,就够了。


  • 遍历图片然后调用设置位置函数的函数——initBox()
    这个函数是三个之中最简单的,所以我决定先写。上面说了图片是不断加载在窗口中下部的位置,那如果图片已经设置在瀑布流之中那还要加载在这个地方吗?答案很明显,肯定不行,所以,我们一定要辨别出在瀑布流和不在瀑布流的图片的区别。那怎么区别呢?我用了个比较笨的方法,就是用类名来区分。如果仔细看了我上面页面的代码的话,就可以发现我的id为main的div所有的子级div都有两个类:pinA和pinF。pinF是用来设置样式的,那pinA是用来干嘛的呢?没错,就是用来辨别图片是否在瀑布流之中的标志。用筛选器来选取有pinA类的div,然后遍历这些div来设置位置。然后图片进行了瀑布流之后,我会把pinA这个类从div中去掉,再次调用这个函数就不会选取到瀑布流中的图片了。这样就不用重复渲染已经在瀑布流中图片的位置了。
    还有一个问题,我发现已经设置好的位置的图片,如果再调用这个函数的话,那位置岂不是又要变化?这样图片位置不断变动,肯定看的人眼都花了吧。所以必须采用方法,区别出已经设置好位置的图片,然后让它保持在固定位置,然后新加载的图片就按照规则摆好。刚刚用了类名来区别过了,现在怎么办?还用类名吗?(虽然说多类名对HTML文档加载速度没什么影响,但是这个操作还是有点麻烦滴)于是我看到了jQuery的data()方法。这个方法是可以在dom元素绑定一个数据供你使用。具体意思就是我们平时写js对象的时候不是可以后面加个“.”然后绑定一个名称,再往里面添加数据自己用嘛(差不多这个意思理解吧),jQuery的这个方法就是这个用途,不过作用的对象是dom元素而已。你可以用jQuery.hasData()来判断dom元素是否绑定了数据。也可以用data()方法来修改数据值。很方便。于是我就用这个方法来区别设置好的图片和没有设置好的图片。好,到这里这个函数的思路都讲完了,下面是代码:
function initBox(jqObj) {//生成照片初始的位置
    var $boxes=jqObj.find('.pinA');//用类名来筛选不在瀑布流的图片
    $boxes.each(function () {
        var $this=$(this);
        if(jQuery.hasData($this[0])){//用hasData方法来区分没有设置过和已经设置过的照片
            setPos($this);     //设置过的直接调用函数            
        }else {
            $this.data('State','new');//没有设置过的图片是没有data数据的,所以设置数据为“new”
            setPos($this);
        }
    })
}

  • 设置具体图片位置的函数setPos()
    上面说过我们设置位置的时候分两种情况:已经设置过的和没有设置过的。我开始的设想是那个图片池是固定在窗口的下部的中央位置的,这种想法很容易联想到用position:fixed的样式来解决,但是,测试过之后我发现,设置成这种样式之后,由于没有其他的内容,导致网页不能滚动起来,而我们的主要函数就是检测网页滚动然后进行瀑布流操作的,所以这种做法并不可行。无奈我只能设置absolute,然后每次图片池都随页面滚动而滚动(然而这样的效果并不好。。。)为了好看,我第一次设置的时候还用了用了fadeIn()和fadeOut()动画。下面是具体代码:
function setPos(jqObj) {//设置图片位置
    if(jqObj.data('State')=='new'){//为没设置过位置的图片设置位置
            var margin_left = Math.floor(- jqObj.outerWidth() / 2 + ((Math.random() * 10) < 5 ? -1 : 1) * (Math.random() * 200));
            var margin_top = Math.floor(- jqObj.outerHeight() / 2 + ((Math.random() * 10) < 5 ? -1 : 1) * (Math.random() * 200));
            jqObj.fadeOut(200);//过度效果
            jqObj.css({
                "position": "absolute",
                "top": winHeight*2 + $(window).scrollTop(),//高度为窗口高度加上滚过的高度
                "left": winWidth/ 2,//距离左边距离为窗口宽度的一半
                "margin-left": margin_left,//设置随机的左边距和上边距达到错乱的效果
                "margin-top": margin_top
            });
            jqObj.fadeIn(200);//过渡效果
            jqObj.data('State','old')//吧设置好的图片的数据改成‘old’表示这图片已经设置过了

    }else if(jqObj.data('State')=='old'){//设置过的图片保持位置,然后随窗口滚动而做滚动动画
        jqObj.animate({
            top: winHeight + $(window).scrollTop()+'px',//高度为窗口高度加上滚过的高度
            left: winWidth/ 2+'px'//距离左边距离为窗口宽度的一半
        },0.000001);//这里时间故意设置很短,不然看起来会很卡
    }
}

  • 瀑布流函数waterfall()
    这是就是按照瀑布流方法写的函数,感觉没什么好说的了吧。具体思路可以看注释:
function WaterFall(jqObj) {//瀑布流函数
    var wid=jqObj.children().eq(0).outerWidth();//取图片固定的宽度
    var cols=Math.floor(winWidth/wid);//算出瀑布流的列数
    if(hArr.length<cols){//hArr是全部变量的数组,用来存储瀑布流各类的高度
        for (var i=0;i<cols;i++) {//这里循环是为了初始化hArr数组
            hArr.push(0);
        }
    }
    jqObj.width(wid*cols).css('margin','0 auto');//设置主体的样式
    var Boxes=jqObj.find('.pinA');//筛选没有进入瀑布流的图片
    Boxes.each(function (index,value) {//遍历图片设置瀑布流
        var h=Boxes.eq(index).outerHeight();
        if(index<cols){//每次设置一行数量的图片
            var newTop;
            var newIndex;
            if($.inArray(0,hArr)>-1){//这里是为了区分第一行的图片和其他行的图片
                newTop=hArr[index];//第一行图片的top全部都是0,其他行的话要根据各列图片的高度来确定
                newIndex=index;
            }else{
                newTop=Math.min.apply(null,hArr);
                newIndex=$.inArray(newTop,hArr);
            }
                $(value).css({//设置样式
                    'position':'absolute',
                    'padding':'15px 0 0 15px',
                    'margin':'0'
                });
                $(value).animate({//设置移动动画
                    top:newTop+'px',
                    left:newIndex*wid+"px"
                },500);
                $(value).removeClass('pinA');
                hArr[newIndex]+=h;
        }
    })
}

到这里主要的函数都搞定了,不过我们还要写一个函数用来做判定,来判定什么时候加载新的图片到图片池里,什么时候加载瀑布流,我觉得写两个函数太烦,所以都封装在一个对象里面了:

var loadingCon={//判断函数
        loadMin:function () {//比较最短的列的大小和窗口一半的高度,作为是否进行加载新图片的判断依据
            if(hArr.length!=0){
                var  minTop=Math.min.apply(null,hArr);
                var scrollTop=$(window).scrollTop();
                var halfWin=Math.floor(winHeight/2);
                return minTop-scrollTop<halfWin;//最短列高度超过窗口一半就返回false
            }else {
                return true;
            }
        },

        loadMax:function () {//比较最长的列和窗口高度一半的大小,作为是否进行瀑布流的判断依据
            if(hArr.length!=0){
                var  maxTop=Math.max.apply(null,hArr);
                var scrollTop=$(window).scrollTop();
                var halfWin=Math.floor(winHeight/2);
                return maxTop-scrollTop<halfWin;//最长列高度超过了窗口一半就返回false
            }else {
                return true;
            }
        }

}

最后来写主要的页面加载函数就大功告成啦:

/**
 * Created by Homer on 2016/7/29.
 */
var winHeight=$(window).height();
var winWidth=$(window).width();
var hArr=[];

$(window).on('load',function () {
    var $main=$("#main");

    initBox($main);
    var dataInt={'data':[{'src':'0.jpg'},{'src':'1.jpg'},{'src':'2.jpg'},{'src':'3.jpg'},{'src':'4.jpg'},{'src':'5.jpg'},{'src':'6.jpg'},{'src':'7.jpg'}]};
   $(window).on('scroll',function () {
        var amount=$main.find('.pinA').length;
       if(!loadingCon.loadMin()&&amount<6){
           $.each(dataInt.data,function (key, value) {
               var oBox=$('<div>').addClass('pinA').addClass('pinF').appendTo($main);
               var oPic=$('<div>').addClass('box').appendTo($(oBox));
               $('<img>').attr('src','images/P_0'+value.src).appendTo($(oPic));
            })
       }
       if (loadingCon.loadMax())WaterFall($main);

       initBox($main);
    })

});

至此这个效果就搞定啦(虽然觉得不太完美。。)
效果1

效果2

其实到这里我也只是完成了半成品而已,还有很多不足的地方,比如窗口往回滚的时候那个图片池会跟着向上滚,然后还有很多不明所以然的闪烁效果。这里给自己一个坑,迟点回来慢慢挖。

其实这篇东西写来也不是打算给别人看的,毕竟我只是个刚入门的小白,还有很多东西都不懂,我写这篇是为了给自己做一个参考,如果你路过看到了,能对你有什么帮助的话,那是我的荣幸;如果里面有什么不正确的内容的话,请一定要留下评论告诉我,我一定很感谢你。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值