3

roundabout效果

效果就像优酷综艺频道页面的图片轮播。
本屌之前做过这个roundabout,参见仿优酷频道首页的图片切换效果,不过用的是类似jquery的库做的。尽管js代码不到200行,但还是显得有点复杂。于是乎,本屌盘算着可不可以用更少的代码完成这个效果。
顺便说一下,如果有读者想造轮子,可以直接看优酷的js代码,基本上都没压缩,封装这个效果的代码在posterTvGrid.js
下面是本屌做出来的效果
http://v.youku.com/v_show/id_XMTM1ODk4NDQyNA==.html

要求

  • 点击左右箭头,图片滚动

  • 点击下面图片索引,如果点击的图片是当前图片或与当前图片相邻的图片,则图片滚动;否则,所有图片先变小,然后点击的图片和与之相邻的图片,在相应位置动态变大。

  • 代码尽可能少,尽可能清晰

布局

    <div id='wrap' ms-controller='roundabout'>
        <div id='roundabout'>
            <ul>
                <li ms-repeat-img='img_list' ms-attr-id='img{{$index}}'>
                    <img ms-attr-src="img/{{img}}.jpeg">
                </li>
            </ul>
        </div>
    </div>

图片的位置有2种情况:

  • 不可见,即不是前面的3张图片,这里类名.hide

.hide {
  width: 530px;
  height: 100px;
  opacity: 0;
  z-index: 0;
  margin-top: 0;
  left: -530px;
}
  • 可见的3张图片,按位置具体可分为.left,.middle,.right

.middle {
  width: 640px;
  height: 270px;
  opacity: 1;
  z-index: 2;
  margin-top: 0;
  left: 275px;
}
.left {
  width: 530px;
  height: 224px;
  opacity: 0.4;
  z-index: 1;
  margin-top: 23px;
  left: 0;
}
.right {
  width: 530px;
  height: 224px;
  opacity: 0.4;
  z-index: 1;
  margin-top: 23px;
  left: 660px;
}

每个类的6个属性都是和图片尺寸(640*270)配合好的,使得动画很自然。当然后面会用到css3 animation,使每张图片的这6个属性进行相应的变化。
下面把上面类添加到相应图片上

    var roundabout=avalon.define({
        $id:'roundabout',
        img_list:[],
        cur:0,//当前页
        ...
    });

cur相对于指针,表示当前中间(可见范围最大)图片是图片列表里的哪张图片,所以会有(从左向右看)

  • cur=>.middle

  • cur+1=>.right

  • cur-1=>.left

  • 其他=>.hide

插值表达式中的逻辑运算符

这里就要把逻辑运算符用到插值表达式里

<div id='roundabout'>
    <ul>
        <li ms-repeat-img='img_list' ms-attr-id='img{{$index}}'
        ms-class-hide='($index<cur-1&&(cur!=img_list.size()-1||$index!=0))||
        ($index>cur+1&&(cur!=0||$index!=img_list.size()-1))' 
        ms-class-left='$index==cur-1||(cur==0&&$index==img_list.size()-1)' 
        ms-class-right='$index==cur+1||(cur==img_list.size()-1&&$index==0)' 
        ms-class-middle='$index==cur' class='animated'>
            <img ms-attr-src="img/{{img}}.jpeg">
        </li>
    </ul>
</div>

解释下
ms-class-middle='$index==cur'
这个很简单,指针的当前位置,添加.middle.

ms-class-left='$index==cur-1||(cur==0&&$index==img_list.size()-1)'
后面部分表示,如果指针在第一张图片那里,就在最后一张图片上添加.left.

ms-class-right='$index==cur+1||(cur==img_list.size()-1&&$index==0)'
和上面类似,后面部分表示,如果指针在最后一张图片那里,就在第一张图片上添加.right.

ms-class-hide='($index<cur-1&&(cur!=img_list.size()-1||$index!=0))||($index>cur+1&&(cur!=0||$index!=img_list.size()-1))'
由两个大或(||)组成

前面部分$index<cur-1&&(cur!=img_list.size()-1||$index!=0)表示在cur-1(左边的可见)图片以左的图片,都会添加.hide隐藏,除开一种情况,当前指针在最后一张图片那里时,图片列表中的第一张图片不能添加.hide,因为它将被添加.right.

这里利用了||运算符的特点,如果第一个条件满足的话,就不会去判定第二个条件;第一个条件不满足时,才会去判定第二个条件。

后面部分$index>cur+1&&(cur!=0||$index!=img_list.size()-1)表示在cur+1(右边的可见)图片以右的图片,都会添加.hide隐藏,除了当前指针在第一张图片那里时,图片列表中的最后一张图片不能添加.hide,因为它将被添加.left.

实际上,用离散数学里的非(p且q)=非p或非q就很好想了,除开已经添加了.left,.middle,.right的图片,剩下的就得添加.hide,$index>cur+1$index<cur-1可以保证$index!=cur.

然后是排除.left,满足.left的条件是$index==cur-1||(cur==0&&$index==img_list.size()-1),它的否定是$index!=cur-1&&(cur!=0||$index!=img_list.size()-1),这就和添加.hide的条件的左边部分一样了。

排除.right也是类似的。

把这两个部分组合起来就可以保证.left,.middle,.right,.hide这4个类之间是互斥的
图片描述
当然也可以利用css优先级,让.left,.middle,.right类的优先级高于.hide,这样添加.hide的判定条件会简单很多。为了严谨些,就没有用这种方式。

圆点序列

        <div id='jump'>
            <ul>
                <li ms-repeat='img_list' ms-class-on='cur==$index'></li>
            </ul>
        </div>

cur==$index让'亮'的圆点和当前指针同步。
到这里,任意改变roundabout.cur=?都可以呈现roundabout布局。

图片滚动

定义css3 animation

滚动过程中,要滚动图片有4种动画状态,以向右滚动为例

  1. 左边可见的图片的上(左)一张图片,不可见=>在可见部分左边,部分可见

  2. 原来左边可见的图片,在可见部分左边,部分可见=>中间,全部可见

  3. 原来中间全部可见的图片,中间,全部可见=>在可见部分右边,部分可见

  4. 原来右边可见的图片,在可见部分右边,部分可见=>不可见
    这里用css3 animation

@keyframes to-right1 {//向右滚动
  0% {
    width: 530px;
    height: 100px;
    opacity: 0;
    z-index: 0;
    margin-top: 85px;
    left: -530px;
  }
  100% {
    width: 530px;
    height: 224px;
    opacity: 0.4;
    z-index: 1;
    margin-top: 23px;
    left: 0;
  }
}
@keyframes to-right2 {
  0% {
    width: 530px;
    height: 224px;
    opacity: 0.4;
    z-index: 1;
    margin-top: 23px;
    left: 0;
  }
  100% {
    width: 640px;
    height: 270px;
    opacity: 1;
    z-index: 2;
    margin-top: 0;
    left: 275px;
  }
}
@keyframes to-right3 {
  0% {
    width: 640px;
    height: 270px;
    opacity: 1;
    z-index: 2;
    margin-top: 0;
    left: 275px;
  }
  100% {
    width: 530px;
    height: 224px;
    opacity: 0.4;
    z-index: 1;
    margin-top: 23px;
    left: 660px;
  }
}
@keyframes to-right4 {
  0% {
    width: 530px;
    height: 224px;
    opacity: 0.4;
    z-index: 1;
    margin-top: 23px;
    left: 660px;
  }
  100% {
    width: 530px;
    height: 100px;
    opacity: 0;
    z-index: 0;
    margin-top: 85px;
    left: 1190px;
  }
}
...

可以看到,动画无非是在.hide,.left,.middle,.right这4个状态间切换。

@keyframes to-right1 {//向右滚动
    0% {
      //.hide
    }
    100% {
      //.left
    }
}
@keyframes to-right2{
    0% {
        //.left
    }
    100% {  
        //.middle
    }
}
@keyframes to-right3{
    0% {
        //middle
    }
    100% {  
        //right
    }
}
@keyframes to-right4{
    0% {
        //right
    }
    100% {  
        //hide1
    }
}
...

为了方便编码,用less

.state(@width,@height,@opacity,@z-index,@margin-top,@left){
    width: @width;
    height: @height;
    opacity: @opacity;
    z-index: @z-index;
    margin-top: @margin-top;
    left:@left;
}
.middle(){
    .state(640px,270px,1,2,0,275px);
}
.left(){
    .state(530px,224px,0.4,1,23px,0);
}
.right(){
    .state(530px,224px,0.4,1,23px,660px);
}
.hide(){
    .state(530px,100px,0,0,85px,-530px);
}
.hide1(){
    .state(530px,100px,0,0,85px,1190px);
}
.middle{
    .middle();
}
.left{
    .left();
}
.right{
    .right();
}
.hide{
    .hide();
}
@keyframes to-right1{//向右滚动
    0% {
        .hide();
    }
    100% {  
        .left();
    }
}
@keyframes to-right2{
    0% {
        .left();
    }
    100% {  
        .middle();
    }
}
@keyframes to-right3{
    0% {
        .middle();
    }
    100% {  
        .right();
    }
}
@keyframes to-right4{
    0% {
        .right();
    }
    100% {  
        .hide1();
    }
}
...

点击左右箭头,图片滚动

    var roundabout=avalon.define({
        $id:'roundabout',
        img_list:[],
        cur:0,//当前页
        jump:function(i,dir){//dir:滚动方向
            roundabout.cur=i;
        }
    });
    <div id='wrap' ms-controller='roundabout'>
        <div id='controls'>
            <a class='left_arrow' href="javascript:;" 
            ms-click='jump(cur==0?img_list.size()-1:cur-1,1)'></a>
            <a class='right_arrow' href="javascript:;" 
            ms-click='jump(cur==img_list.size()-1?0:cur+1,2)'></a>
        </div>
        ...
    </div>

jump方法的dir参数后面会用到。
这里还没有添加动画类,只能进行没有动画的滚动。
图片描述

添加动画

//所有的动画类
var animate_class='to-right1 to-right2 to-right3 to-right4 to-left1 to-left2 to-left3 to-left4';
...
jump:function(i,dir){
    if(dir==1){//向右滚 cur-1
        avalon($('img'+prev(i))).removeClass(animate_class).addClass('to-right1');
        avalon($('img'+i)).removeClass(animate_class).addClass('to-right2');
        avalon($('img'+next(i))).removeClass(animate_class).addClass('to-right3');
        avalon($('img'+next(next(i)))).removeClass(animate_class).addClass('to-right4');
    }else{//向左滚 cur+1
        avalon($('img'+next(i))).removeClass(animate_class).addClass('to-left1');
        avalon($('img'+i)).removeClass(animate_class).addClass('to-left2');
        avalon($('img'+prev(i))).removeClass(animate_class).addClass('to-left3');
        avalon($('img'+prev(prev(i)))).removeClass(animate_class).addClass('to-left4');
    }
    roundabout.cur=i;
}
...
function prev(now){//上一页
    return now==0?roundabout.img_list.size()-1:now-1;
}
function next(now){//下一页
    return now==roundabout.img_list.size()-1?0:now+1;
}

几点说明

  • 这里的removeClass方法和jquery中的一样,当传入参数像上面animate_class变量,多个类以空格分开时,对目标删除多个类(如果有的话)。

  • prev,next方法用来得到上一张图片和下一张图片的在图片列表中的索引.注意next(next(i))不等于next(i+1).

  • 添加动画类之前,要先删除所有可能存在的动画类。

点击圆点,图片滚动

和点击左右箭头,图片滚动的动画不一样,这里对目标图片及其左右图片是一个从小变大的动画效果。

.state(@width,@height,@opacity,@z-index,@margin-top,@left){
    width: @width;
    height: @height;
    opacity: @opacity;
    z-index: @z-index;
    margin-top: @margin-top;
    left:@left;
}
.jump-state(){
    .state(0,0,0,0,135px,585px);
}
@keyframes jump-prev{//变小->左边图片
    0% {
        .jump-state();
    }
    100% {  
        .left();
    }
}
@keyframes jump-middle{//变小->中间图片
    0% {
        .jump-state();
    }
    100% {  
        .middle();
    }
}
@keyframes jump-next{//变小->右边图片
    0% {
        .jump-state();
    }
    100% {  
        .right();
    }
}

下面对圆点绑定点击事件

<div id='jump'>
    <ul>
        <li ms-repeat='img_list' ms-class-on='cur==$index' ms-click='spec_jump($index)'></li>
    </ul>
</div>
spec_jump:function(i){
    var cur=roundabout.cur;
    if(cur-prev(i)==0||cur-next(i)==0){//如果点击的图片是当前图片或当前图片的相邻图片
        var dir=1;//确定滚动方向
        if(cur-prev(i)==0)
            dir=2;
        roundabout.jump(i,dir);
    }else{
        roundabout.cur=i;
        avalon($('img'+prev(i))).removeClass(animate_class).addClass('jump-prev');
        avalon($('img'+i)).removeClass(animate_class).addClass('jump-middle');
        avalon($('img'+next(i))).removeClass(animate_class).addClass('jump-next');
    }
}

这里会添加jump-prev,jump-middle,jump-next类,所以前面的animate_class变量也要加上这3个类。

后记

js代码不超过50行,当然css代码多点,不过用less写的话,编程体验很好


下载


TheViper
465 声望16 粉丝