zoukankan      html  css  js  c++  java
  • 【IScroll深入学习】解决IScroll疑难杂症

    前言

    在去年,我们对IScroll的源码进行了学习,并且分离出了一段代码自己使用,在使用学习过程中发现几个致命问题:

    ① 光标移位

    ② 文本框找不到(先让文本框获取焦点,再滑动一下,输入文字便可重现)

    ③ 偶尔导致头部消失,头部可不是fixed哦

     

    由于以上问题,加之去年我们团队的工作量极大,和中间一些组织架构调整,这个事情一直被放到了今天,心里一直对此耿耿于怀,因为IScroll让人忘不了的好处

    小钗坚信,IScroll可以带来前端体验上的革命,因为他可以解决以下问题

    • 区域滑动顺滑感的体验
    • 解决fixed移位问题
    • 解决动画过程中长短页的问题,并且可以优化view切换动画的顺畅度

    我们不能因为一两个小问题而放弃如此牛逼的点子,所以我们要处理其中的问题,那么这些问题是否真的不可解决,而引起这些问题的原因又到底是什么,我们今天来一一探索

    PS:该问题已有更好的解决方案,待续

    抽离IScroll

    第一步依旧是抽离IScroll核心逻辑,我们这里先在简单层面上探究问题,以免被旁枝末节的BUG困扰,这里形成的一个库只支持纵向滚动,代码量比较少

    Demo
    核心代码

    代码中引入了fastclick解决其移动端点击问题,demo效果在此:

    http://sandbox.runjs.cn/show/xq2fbetv

    基本代码出来了,我们现在来一个个埋坑,首先解决难的问题!

    光标跳动/文本框消失

    光标跳动是什么现象大家都知道了,至于导致的原因又我们测试下来,即可确定罪魁祸首为:transform,于是我们看看滑动过程中发生了什么

    ① 每次滑动会涉及到位置的变化

    this._translate(0, newY);

    ② 每次变化会改变transform属性

     1 //移动x,y这里比较简单就不分离y了
     2 _translate: function (x, y) {
     3   this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
     4 
     5   this.x = x;
     6   this.y = y;
     7 
     8   if (this.options.scrollbars) {
     9     this.indicator.updatePosition();
    10   }
    11 
    12 },

    我们这里做一次剥离,将transform改成直接改变top值看看效果

    this.scrollerStyle['top'] = y + 'px';

    而事实证明,一旦去除transform属性,我们这里便不再有光标闪动的问题了。

    更进一步的分析,实验,你会发现其实引起的原因是这句:

    //       this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
           this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' ;

    没错,就是css3d加速引起的,他的优势是让动画变得顺畅,却不曾想到会引起文本框光标闪烁的问题

    针对ios闪烁有一个神奇的属性是

    -webkit-backface-visibility: hidden;

    于是加入到,scroller元素上后观察之,无效,舍弃该方案再来就是一些怪办法了:

    滑动隐藏虚拟键盘

    文本获取焦点的情况下,会隐藏虚拟键盘,连焦点都没有了,这个问题自然不药而愈,于是我们只要滑动便让其失去焦点,这样似乎狡猾的绕过了这个问题

    在touchmove逻辑处加入以下逻辑

     1 //暂时只考虑input问题,有效再扩展
     2 var el = document.activeElement;
     3 if (el.nodeName.toLowerCase() == 'input') {
     4   el.blur();
     5   this.disable();
     6   setTimeout($.proxy(function () {
     7     this.enable();
     8   }, this), 250);
     9   return;
    10 }

    该方案最为简单粗暴,他在我们意图滑动时便直接导致虚拟键盘失效,从而根本不会滑动,便错过了光标跳动的问题

    甚至,解决了由于滚动导致的文本框消失问题!!!

    其中有一个250ms的延时,这个期间是虚拟键盘隐藏所用时间,这个时间段将不可对IScroll进行操作,该方案实验下来效果还行

    其中这个延时在200-300之间比较符合人的操作习惯,不设置滚动区域会乱闪,取什么值各位自己去尝试,测试地址:

    http://sandbox.runjs.cn/show/8nkmlmz5

    这个方案是我觉得最优的方案,其是否接受还要看产品态度

    死磕-重写_translate

    _translate是IScroll滑动的总控,这里默认是使用transform进行移动,但若是获取焦点的情况下我们可以具有不一样的方案

    在文本框具有焦点是,我们使用top代替transform!

    PS:这是个烂方法不建议采用

     1 //移动x,y这里比较简单就不分离y了
     2 _translate: function (x, y) {
     3 
     4   var el = document.activeElement;
     5   if (el.nodeName.toLowerCase() == 'input') {
     6     this.scrollerStyle['top'] = y + 'px';
     7   } else {
     8     this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
     9   }
    10 
    11   this.x = x;
    12   this.y = y;
    13 
    14   if (this.options.scrollbars) {
    15     this.indicator.updatePosition();
    16   }
    17 
    18 },

    该方案被测试确实可行,不会出现光标闪的现象,但是有一个问题却需要我们处理,便是一旦文本框失去焦点,我们要做top与transform的换算

    所以这是一个烂方法!!!这里换算事实上也不难,就是将top值重新归还transform,但是整个这个逻辑却是让人觉得别扭

    而且我们这里还需要一个定时器去做计算,判断何时文本框失去焦点,整个这个逻辑就是一个字 坑!

     1 //移动x,y这里比较简单就不分离y了
     2 _translate: function (x, y) {
     3 
     4   var el = document.activeElement;
     5   if (el.nodeName.toLowerCase() == 'input') {
     6     this.scrollerStyle['top'] = y + 'px';
     7 
     8     //便需要做距离换算相关清理,一旦文本框事情焦点,我们要做top值还原
     9     if (!this.TimerSrc) {
    10       this.TimerSrc = setInterval($.proxy(function () {
    11         var el = document.activeElement;
    12         if (el.nodeName.toLowerCase() != 'input') {
    13 
    14           pos = this.getComputedPosition();
    15 
    16           var top = $(scroller).css('top');
    17           this.scrollerStyle['top'] = '0px';
    18           console.log(pos);
    19 
    20           var _x = Math.round(pos.x);
    21           var _y = Math.round(pos.y);
    22           _y = _y + parseInt(top);
    23 
    24           //移动过去
    25           this._translate(_x, _y);
    26 
    27           clearInterval(this.TimerSrc);
    28           this.TimerSrc = null;
    29         }
    30       }, this), 20);
    31     }
    32   } else {
    33     this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
    34   }
    35 
    36   this.x = x;
    37   this.y = y;
    38 
    39   if (this.options.scrollbars) {
    40     this.indicator.updatePosition();
    41   }
    42 
    43 },

    经测试,该代码可以解决光标跳动问题,但是坑不坑大家心里有数,一旦需要被迫使用定时器的地方,必定会有点坑!测试地址

    http://sandbox.runjs.cn/show/v9pno9d8

    死磕-文本框消失

    文本框消息是由于滚动中产生动画,将文本框搞到区域外了,这个时候一旦我们输入文字,导致input change,系统便会自动将文本定位到中间,而出现文本不可见问题

    该问题的处理最好的方案,依旧是方案一,若是这里要死磕,又会有许多问题,方案无非是给文本设置changed事件,或者定时器什么的,当变化时,自动将文本元素

    至于IScroll可视区域,楼主这里就不献丑了,因为我基本决定使用方案一了。

    步长移动

    所谓步长移动便是我一次必须移动一定距离,这个与图片横向轮播功能有点类似,而这类需求在移动端数不胜数,那我们的IScroll应该如何处理才能加上这一伟大特性呢?

    去看IScroll的源码,人家都已经实现了,居然人家都实现了,哎,但是我们这里不管他,照旧做我们的事情吧,加入步长功能

    PS:这里有点小小的失落,我以为没有实现呢,这样我搞出来肯定没有官方的优雅了!

    思路

    思路其实很简单,我们若是设置了一个步长属性,暂时我们认为他是一个数字(其实可以是bool值,由库自己计算该值),然后每次移动时候便必须强制移动该属性的整数倍即可,比如:

    1 var s = new IScroll({
    2   wrapper: $('#wrapper'),
    3   scroller: $('#scroller'),
    4   setp: 40
    5 });

    这个便要求每次都得移动10px的步长,那么这个如何实现呢?其实实现点,依然是_translate处,我们这里需要一点处理

     1 //移动x,y这里比较简单就不分离y了
     2 _translate: function (x, y, isStep) {
     3 
     4   //处理步长
     5   if (this.options.setp && !isStep) {
     6     var flag2 = y > 0 ? 1 : -1; //这个会影响后面的计算结果
     7     var top = Math.abs(y);
     8     var mod = top % this.options.setp;
     9     top = (parseInt(top / this.options.setp) * this.options.setp + (mod > (this.options.setp/2) ? this.options.setp : 0)) * flag2;
    10     y = top;
    11   }
    12 
    13   this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
    14 
    15   this.x = x;
    16   this.y = y;
    17 
    18   if (this.options.scrollbars) {
    19     this.indicator.updatePosition();
    20   }
    21 
    22 },

    这样一改后,每次便要求移动40px的步长,当然,我这里代码写的不是太好,整个效果在此

    这里唯一需要处理的就是touchmove了,每次move的时候,我们不应该对其进行步长控制,而后皆可以,这种控制步长的效果有什么用呢?请看这个例子:

    双IScroll的问题

    所谓双IScroll,便是一个页面出现了两个IScroll组件的问题,这个问题前段时间发生在了我们一个团队身上,其状况具体为

    他在一个view上面有两个地方使用了IScroll,结果就是感觉滑动很卡,并且不能很好的定位原因,其实导致这个原因的主要因素是:

    他将事件绑定到了document上,而不是具体包裹层元素上,这样的话,就算一个IScroll隐藏了,他滑动时候已经执行了两个逻辑,从而出现了卡的现象

    当然,一个IScroll隐藏的话其实应该释放其中的事件句柄,当时他没有那么做,所以以后大家遇到对应的功能,需要将事件绑定对位置

    我们这里举个例子:

      1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      2 <html xmlns="http://www.w3.org/1999/xhtml">
      3 <head>
      4   <title></title>
      5   <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
      6   <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
      7   <meta name="keywords" content="" />
      8   <meta name="description" content="" />
      9   <meta name="robots" content="all" />
     10   <style type="text/css">
     11         
     12 /*reset*/
     13 body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{margin:0;padding:0}
     14 body,button,input,select,textarea{font:normal 14px/1.5 Tahoma,"Lucida Grande",Verdana,"Microsoft YaHei",hei;}
     15 article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,iframe{display:block;}
     16 h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:500;}
     17 address,cite,dfn,em,var,i{font-style:normal;font-weight:normal;font-family:arial;}
     18 ul,ol{list-style:none}
     19 a{color:#000;text-decoration:none}
     20 a:hover{text-decoration:underline;-webkit-transition:color .2s linear;-moz-transition:color .2s linear;-ms-transition:color .2s linear;-o-transition:color .2s linear;transition:color .2s linear;}
     21 fieldset,img,button,input{border:0}
     22 button,input,select,textarea{font-size:100%}
     23 table{border-collapse:collapse;border-spacing:0}
     24 input[type="button"],input[type="submit"]{-webkit-appearance:none;}
     25 body{min-width:320px; background:#f5f5f5;overflow-x:hidden;-webkit-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-font-smoothing:subpixel-antialiased;-moz-user-select:none;color:#000;}:focus{outline:none;}.clearfix:after{clear:both;content:'.';display:block;height:0;visibility:hidden;line-height:0;}.clearfix{*zoom:1;}.fl{float:left;}.fr{float:right;}.clear{clear:both;}.overflow{overflow:hidden;}.ellips{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}.ellips_line2,.ellips_line3{display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;}.ellips_line2{-webkit-line-clamp:2;}.ellips_line3{-webkit-line-clamp:3;}.pos_rel{position:relative;}
     26 html,body,.h100{ height:100%; }
     27 dfn{color:#ff9913; font-size:12px; font-weight:normal;}
     28 /*-----------------------------------------common--------------------*/
     29 .i,.i_bef:before,.i_aft:after,.carlist li:before,.ok_crt:after{background:url(../img/style1/v2_bgs.png) no-repeat; background-size:175px 125px;}
     30 .i_bef:before,.i_aft:after,.carlist li:before{position: absolute;content: "";}
     31 .abs_size{-moz-box-sizing:border-box; -webkit-box-sizing:border-box; box-sizing:border-box;}
     32 .opacity{opacity:0.7; filter:alpha(opacity=70);}
     33 
     34 /*active*/
     35 .list_st_border li:active,.p_hinttxt:active,.search_cancel:active,.citylist dd:active{ background:#f8f8f8;}
     36 .btn_yellow:active{background:#eb840f;}
     37 
     38 /*layout*/
     39 .wrap{margin-top:48px;}
     40 .wrap_pb{margin-top:48px; padding-bottom:45px;}
     41 .p10{padding:10px;}
     42 .hm{text-align:center;}
     43 .break_all{word-break:break-all;}
     44 .fix_b{position:fixed; bottom:0; left:0;z-index:9999;}
     45 
     46 /*font bg color size*/
     47 h1{font:normal 1.286em/1.5 "";}/*18px*/
     48 h2{font:normal 1.143em/1.5 "";}/*16px*/
     49 h3{font:600 1em/1.5 "";}/*14px*/
     50 .price{margin-left:5px;}
     51 .price i{margin-left:2px; font-size:1.286em;}
     52 .greyfont{color:#686868;}
     53 .greyfont1{color:#909090;}
     54 .bggrey{height:100%; background:#f5f5f5;}
     55 
     56 /*header*/
     57 header{position:fixed; top:0; left:0; z-index:9999; width:100%; height:48px;background-color:#1491c5;}
     58 .returnico{position:absolute; left:0; top:0;  width:68px; height:48px; background-color:#15a4d5;}
     59 .returnico:before{left:25px;top:16px;width:12px;height:20px; background-position:0 0;}
     60 .icon_phone,.icon_home{width:42px;height:100%;top:0;position:relative;float:right;}
     61 .icon_phone:before{width:11px;height:20px;top:16px;right:12px;background-position:-115px -65px;}
     62 .icon_home:before{width:20px;height:19px;top:16px;right:10px;background-position:-83px -66px;}
     63 header i:active{opacity:0.7; filter:alpha(opacity=70);}
     64 header h1{position:absolute; width:100%;line-height:48px; text-align:center; letter-spacing:2px; color: #fff;}
     65 .header_r,.header_rs{position:absolute;z-index:9; top:0; right:0; line-height:48px; padding:0 15px; font-size:18px; background:#15a4d5; color:#fff;}
     66 .header_rs{padding:0 5px; font-size:12px; }
     67 
     68 /*background-position*/
     69 .select_n:before{width:20px;height:20px;background-position:-18px 0;}
     70 .select_n.current:before{background-position:-44px 0;}
     71 
     72 /*searchbox*/
     73 .search_wrap{overflow:hidden; padding:10px; background:#dfeaf1;}
     74 .search_box{position:relative; float:left; width:100%;}
     75 .search_input{width:100%; padding:0 10px 0 28px; line-height:30px; background-color:#fff; border-radius:4px; color:#ccc;}
     76 .fdj:before,.fdj:after,.search_box:before,.search_box:after{position:absolute; content:""; z-index:9;}
     77 .fdj:before,.search_box:before{left:5px; top:7px; width:12px; height:12px; border:1px solid #bcbcbc; border-radius:12px;}
     78 .fdj:after,.search_box:after{left:19px; top:17px; width:1px; height:8px; background:#bcbcbc;}
     79 .search_cancel{ display:none;float:left; width:20%; line-height:30px; text-align:center; font-size:16px;color:#1491c5; background:none; border:none;}
     80 .close_icon{display:none; position:absolute; z-index:10; top:8px; right:4px; width:16px; height:16px; border-radius:30px; background:#b1b1b1;}
     81 .close_icon:before,.close_icon:after{position:absolute; content:""; top:4px; left:7px; width:2px; height:8px; background:#fff; }
     82 .close_icon:before{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
     83 .fdj:after,.search_box:after,.close_icon:after{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
     84 .search_focus .search_box{width:80%;}
     85 .search_focus .close_icon, .search_focus .search_cancel{display:block;}
     86 .search_input:focus{color:#000;}
     87 
     88 /*tab*/
     89 .tab{ background-color:#f8f8f8; border-bottom:1px solid #dfdfdf;}
     90 .tab li{float:left; width:50%; height:39px; line-height:39px; text-align:center; border-right:1px solid #dfdfdf;}
     91 .tab li:last-child{border-right:none;}
     92 .tab li.tabcrt{ background-color:#dfdfdf;}
     93 
     94 .tab_b{ background-color:#f5f5f5; border-bottom:1px solid #c1c1c1;}
     95 .tab_b li{float:left; width:50%; height:39px; line-height:39px; text-align:center;}
     96 .tab_b li:last-child{border-right:none;}
     97 .tab_b li.tabcrt{color:#1491c5; border-bottom:4px solid #1491c5;}
     98 
     99 /*list*/
    100 .list_st_border{ background:#fff; border-bottom:none; border:1px solid #cfcfcf; }
    101 .list_st_border li{position:relative;padding:0 10px; line-height:42px; border-bottom:1px solid #cfcfcf;}
    102 .list_st_border li:last-child{border-bottom:none;}
    103 
    104 .list_sli{padding:10px; overflow:hidden; border-bottom:1px solid #cfcfcf; background:#fff;}
    105 .list_sli .list_sunit{float:left;}
    106 
    107 .citylist{color:#000;}
    108 .citylist dt,.citylist dd{padding-left:10px;border-bottom:1px solid #e2e2e2;}
    109 .citylist dt{line-height:25px; background-color:#eaeaea;}
    110 .citylist dd{position:relative; font-size:16px; line-height:43px; background-color:#fff;}
    111 .citylist .ok_crt{color:#1491c5;}
    112 .citylist .ok_crt:after{position:absolute;content:""; right:10px; top:50%; margin-top:-6px; width:12px; height:13px; background-position:-110px 0;}
    113 
    114 
    115 /*arr*/
    116 .li_arr_r{position:relative;}
    117 .arr_r{position:absolute;right:0px; top:50%; width:30px; height:30px; margin-top:-15px; }
    118 .arr_r:before,.arr_r:after,.li_arr_r:before,.li_arr_r:after{position: absolute; left:15px; content: "";width:2px; height:7px;  background-color:#909090;}
    119 .arr_r:before,.li_arr_r:before{top:10px;-webkit-transform:rotate(-45deg); -moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
    120 .arr_r:after,.li_arr_r:after{top:15px;-webkit-transform:rotate(45deg); -moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
    121 .li_arr_r:before,.li_arr_r:after{left:auto; right:10px; top:50%; margin-top:-5px;}
    122 .li_arr_r:after{margin-top:0;}
    123 
    124 /*p*/
    125 .p_grey{margin:10px 5px; font-size:13px; color:#989898;}
    126 .p_grey_center{ text-align:center; margin:20px 5px; font-size:13px; color:#989898;}
    127 .p_hinttxt{padding:20px 10px; text-align:center; font-size:16px;  color:#1491c5;}
    128 
    129 
    130 /*btn*/
    131 .btn_yellow,.btn_del{width:100%; height:44px; line-height:42px; padding:0 10px;  color:#fff;}
    132 .btn_yellow{background:#ff9913;}
    133 .btn_del{background:#ca4345;text-align:center;font-size:1.2em;}
    134 
    135 .btn_pay{padding:0 20px;height:44px;float:right;background:#ff7d13;color:#fff;}
    136 .btn_pay:active{background:#ff7300;}
    137 .room_num {position:absolute; right:10px; width:100px; height:30px; line-height:30px;color:#000;background-color: #fff; text-align:center;border:#bfbfbf 1px solid;}
    138 .room_num i{position:absolute; width:30px; height:30px; text-align:center; font:normal 2em/28px "Arial";}
    139 .room_num i:first-child{left:0;background:#f4f4f4;color:#d9d9d9;font:normal 2.8em/25px "Arial";}
    140 .room_num i:last-child{right:0;background:#06a2d0;color:#fff;}
    141 
    142 
    143     /*日历*/
    144 .cui_cldwrap{color:#000;}
    145 .cui_cldweek{height:25px; overflow:hidden;font:normal 12px/24px "verdana";border-bottom:1px solid #c8c8c8;}
    146 .cui_cldweek li{float:left; width:14%; text-align:center; }
    147 .cui_cldweek li:first-child,.cui_cldweek li:last-child{width:15%; color:#acacac;}
    148 .cui_cldmonth{height:35px;text-align:center;font:normal 16px/35px "verdana"; background:#fff;}
    149 .cui_cldunit{margin-bottom:20px;}
    150 .cui_cld_daybox{overflow:hidden; background:#fff;}
    151 .cui_cld_daybox li{float:left; width:14%; height:45px; padding:4px 0; font:normal 12px/45px "verdana"; overflow:hidden; text-align:center;}
    152 .cui_cld_daybox li:nth-child(7n),.cui_cld_daybox li:nth-child(7n+1){width:15%;}
    153 .cui_cld_daypass{ background:#f7f7f7;}
    154 .cui_cld_daycrt{background:#06a2d0; color:#fff;}
    155 .cui_cld_dayfuture{background:#fff;}
    156 .cui_cld_day_nocrtmonth{ visibility:hidden;}
    157 .cui_cld_day_havetxt em{display:block; line-height:25px;}
    158 .cui_cld_day_havetxt i{display:block;line-height:15px;}
    159 .cui_cld_day_hint{color:#06a2d0;}
    160 /*全局XXXX*/
    161 
    162 /*弹出蓝色框*/
    163 .cui-pop-box{background:#fff;}
    164 .cui-text-center{text-align:center;}
    165 .cui-pop-box .cui-hd{height:40px;line-height:40px;font-size:1.2em;color:#fff;background:#1491c5;padding:0 10px;position:relative;}
    166 .cui-pop-box .cui-hd .cui-close{position:absolute;top:10px; right:10px; width:18px; height:18px;line-height:18px;border-radius:50%; background:#fff;color:#1491c5;text-align:center;font-weight:bold;}
    167 .cui-error{width:150px;margin:auto;border-radius:5px;background:rgba(73,73,73,1);padding:10px;color:#fff;font-weight:700;text-align:center;word-break:break-all;}
    168 .cui-select-view li{border-bottom:#dfdfdf 1px solid;padding:.8em 2em .8em .8em;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;position:relative;}
    169 .cui-select-view li.current{color:#1084bc;}
    170 .cui-select-view li:active{background:rgba(0,0,0,.05);}
    171 .cui-select-view li.current:before,.cui-select-view li.current:after{position:absolute;content:"";background:#1084bc;height:3px;top:50%;border-radius:3px;}
    172 .cui-select-view li.current:before{width:18px;right:10px;margin-top:-2px;-webkit-transform:rotate(-50deg);-moz-transform:rotate(-50deg);-ms-transform:rotate(-50deg);transform:rotate(-50deg);}
    173 .cui-select-view li.current:after{width:9px;right:22px;margin-top:2px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}
    174 
    175 .cui-roller{width:100%;height:90px;overflow:hidden;position:relative;display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex;}
    176 .cui-roller .ul-list{text-align:center;font-size:1.1em;line-height:30px;-webkit-box-flex: 1;-moz-box-flex: 1;-webkit-flex: 1;-ms-flex: 1;flex: 1; position: absolute; left: 50%; margin-left: -23px; }
    177 .cui-mask{position:absolute;z-index:3;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;
    178     background: linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
    179     background: -webkit-gradient(linear,left bottom,left top,from(#fff),color-stop(0.52,rgba(245,245,245,0)),color-stop(0.48,rgba(245,245,245,0)),to(#fff));
    180     background: -webkit-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
    181     background: -moz-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
    182 }
    183 .cui-lines{width:100%;height:34px;position:absolute;top:50%;margin-top:-17px;border-top:#dfdfdf 1px solid;border-bottom:#dfdfdf 1px solid;}
    184 .cui-roller-tips{color:#8d8d8d;padding:5px;text-align:center;}
    185 .cui-roller-btns{background:#f6f5f5;padding:10px;text-align:center;}
    186 .cui-roller-btns span{width:45%;display:inline-block;padding:10px 0;color:#fff;}
    187 .cui-roller-btns span:active{opacity:.75;}
    188 .cui-roller-btns .cui-btns-cancel{background:#a9a9a9;margin-right:5%;}
    189 .cui-roller-btns .cui-btns-sure{background:#ff9913;}
    190 
    191     </style>
    192   <script id="others_zepto_10rc1" type="text/javascript" class="library" src="http://sandbox.runjs.cn/js/sandbox/other/zepto.min.js"></script>
    193   <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/279/2h5lvbt5/fastclick.js"></script>
    194 </head>
    195 <body>
    196   <div class="cui-pop-box">
    197     <div class="cui-hd">
    198       <div class="cui-text-center">
    199         滚轮滚轮滚轮</div>
    200     </div>
    201     <div class="cui-bd">
    202       <div class="cui-roller" >
    203       <div id="wrapper" style=" 100px; position: relative;  ">
    204          <ul class="ul-list" id="scroller" style="left: 40%;  100px;">
    205           <li>选项1</li>
    206           <li>选项2</li>
    207           <li>选项3</li>
    208           <li>选项4</li>
    209           <li>选项5</li>
    210           <li>选项6</li>
    211           <li>选项7</li>
    212           <li>选项8</li>
    213           <li>选项9</li>
    214           <li>选项10</li>
    215         </ul>
    216       </div>
    217      
    218         <div id="wrapper2" style=" 100px; position: relative;">
    219           <ul class="ul-list" id="scroller2" style="right: 10%;  100px;">
    220             <li>选项11</li>
    221             <li>选项12</li>
    222             <li>选项13</li>
    223             <li>选项14</li>
    224             <li>选项15</li>
    225             <li>选项16</li>
    226             <li>选项17</li>
    227             <li>选项18</li>
    228             <li>选项19</li>
    229             <li>选项20</li>
    230           </ul>
    231         </div>
    232    
    233         <div class="cui-lines">
    234           &nbsp;</div>
    235     </div>
    236       <p class="cui-roller-tips">
    237         提示信息</p>
    238       <div class="cui-roller-btns">
    239         <span class="cui-btns-cancel">取消</span><span class="cui-btns-sure">确定</span>
    240       </div>
    241     </div>
    242   </div>
    243   <script type="text/javascript">
    244 
    245     var utils = (function () {
    246       var me = {};
    247       var _elementStyle = document.createElement('div').style;
    248 
    249       //获得需要兼容CSS3前缀
    250       var _vendor = (function () {
    251         var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'];
    252         var transform;
    253         var i = 0;
    254         var l = vendors.length;
    255 
    256         for (; i < l; i++) {
    257           transform = vendors[i] + 'ransform';
    258           if (transform in _elementStyle) return vendors[i].substr(0, vendors[i].length - 1);
    259         }
    260         return false;
    261       })();
    262 
    263       //获取样式(CSS3兼容)
    264       function _prefixStyle(style) {
    265         if (_vendor === false) return false;
    266         if (_vendor === '') return style;
    267         return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
    268       }
    269 
    270       me.getTime = Date.now || function getTime() { return new Date().getTime(); };
    271 
    272       me.addEvent = function (el, type, fn, capture) {
    273         if (el[0]) el = el[0];
    274         el.addEventListener(type, fn, !!capture);
    275       };
    276 
    277       me.removeEvent = function (el, type, fn, capture) {
    278         if (el[0]) el = el[0];
    279         el.removeEventListener(type, fn, !!capture);
    280       };
    281 
    282       /*
    283       current:当前鼠标位置
    284       start:touchStart时候记录的Y(可能是X)的开始位置,但是在touchmove时候可能被重写
    285       time: touchstart到手指离开时候经历的时间,同样可能被touchmove重写
    286       lowerMargin:y可移动的最大距离,这个一般为计算得出 this.wrapperHeight - this.scrollerHeight
    287       wrapperSize:如果有边界距离的话就是可拖动,不然碰到0的时候便停止
    288       */
    289       me.momentum = function (current, start, time, lowerMargin, wrapperSize) {
    290         var distance = current - start,
    291         speed = Math.abs(distance) / time,
    292         destination,
    293         duration,
    294         deceleration = 0.0006;
    295 
    296         destination = current + (speed * speed) / (2 * deceleration) * (distance < 0 ? -1 : 1);
    297         duration = speed / deceleration;
    298 
    299         if (destination < lowerMargin) {
    300           destination = wrapperSize ? lowerMargin - (wrapperSize / 2.5 * (speed / 8)) : lowerMargin;
    301           distance = Math.abs(destination - current);
    302           duration = distance / speed;
    303         } else if (destination > 0) {
    304           destination = wrapperSize ? wrapperSize / 2.5 * (speed / 8) : 0;
    305           distance = Math.abs(current) + destination;
    306           duration = distance / speed;
    307         }
    308 
    309         return {
    310           destination: Math.round(destination),
    311           duration: duration
    312         };
    313 
    314       };
    315 
    316       $.extend(me, {
    317         hasTouch: 'ontouchstart' in window
    318       });
    319 
    320 
    321       //我们暂时只判断touch 和 mouse即可
    322       $.extend(me.style = {}, {
    323         transform: _prefixStyle('transform'),
    324         transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
    325         transitionDuration: _prefixStyle('transitionDuration'),
    326         transitionDelay: _prefixStyle('transitionDelay'),
    327         transformOrigin: _prefixStyle('transformOrigin')
    328       });
    329 
    330       $.extend(me.eventType = {}, {
    331         touchstart: 1,
    332         touchmove: 1,
    333         touchend: 1,
    334 
    335         mousedown: 2,
    336         mousemove: 2,
    337         mouseup: 2
    338       });
    339 
    340       $.extend(me.ease = {}, {
    341         quadratic: {
    342           style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
    343           fn: function (k) {
    344             return k * (2 - k);
    345           }
    346         },
    347         circular: {
    348           style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
    349           fn: function (k) {
    350             return Math.sqrt(1 - (--k * k));
    351           }
    352         },
    353         back: {
    354           style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
    355           fn: function (k) {
    356             var b = 4;
    357             return (k = k - 1) * k * ((b + 1) * k + b) + 1;
    358           }
    359         },
    360         bounce: {
    361           style: '',
    362           fn: function (k) {
    363             if ((k /= 1) < (1 / 2.75)) {
    364               return 7.5625 * k * k;
    365             } else if (k < (2 / 2.75)) {
    366               return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
    367             } else if (k < (2.5 / 2.75)) {
    368               return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
    369             } else {
    370               return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
    371             }
    372           }
    373         },
    374         elastic: {
    375           style: '',
    376           fn: function (k) {
    377             var f = 0.22,
    378         e = 0.4;
    379 
    380             if (k === 0) { return 0; }
    381             if (k == 1) { return 1; }
    382 
    383             return (e * Math.pow(2, -10 * k) * Math.sin((k - f / 4) * (2 * Math.PI) / f) + 1);
    384           }
    385         }
    386       });
    387       return me;
    388     })();
    389 
    390     function IScroll(opts) {
    391       this.wrapper = typeof opts.wrapper == 'string' ? $(opts.wrapper) : opts.wrapper;
    392       this.scroller = typeof opts.scroller == 'string' ? $(opts.scroller) : opts.scroller;
    393       if (!opts.wrapper[0] || !opts.scroller[0]) throw 'param error';
    394 
    395       this.wrapper = this.wrapper[0];
    396       this.scroller = this.scroller[0];
    397 
    398       //这个属性会被动态改变的,如果这里
    399       this.scrollerStyle = this.scroller.style;
    400 
    401       this.options = {
    402         //每次要求移动的步长
    403         setp: false,
    404         //是否具有滚动条
    405         scrollbars: true,
    406         // 其实时期Y的位置
    407         startY: 0,
    408         //超出边界还原时间点
    409         bounceTime: 600,
    410         //超出边界返回的动画
    411         bounceEasing: utils.ease.circular,
    412 
    413         //超出边界时候是否还能拖动
    414         bounce: true,
    415 
    416         bindToWrapper: true,
    417 
    418         //当window触发resize事件60ms后还原
    419         resizePolling: 60,
    420         startX: 0,
    421         startY: 0
    422       };
    423 
    424       for (var i in opts) {
    425         this.options[i] = opts[i];
    426       }
    427 
    428       this.translateZ = ' translateZ(0)';
    429 
    430       this.x = 0;
    431       this.y = 0;
    432       this._events = {};
    433       this._init();
    434 
    435       //更新滚动条位置
    436       this.refresh();
    437 
    438       //更新本身位置
    439       this.scrollTo(this.options.startX, this.options.startY);
    440 
    441       this.enable();
    442 
    443     };
    444 
    445     IScroll.prototype = {
    446       _init: function () {
    447         this._initEvents();
    448 
    449         //初始化滚动条,滚动条此处需要做重要处理
    450         if (this.options.scrollbars) {
    451           this._initIndicator();
    452         }
    453       },
    454       refresh: function () {
    455         var rf = this.wrapper.offsetHeight;     // Force reflow
    456 
    457         this.wrapperHeight = this.wrapper.clientHeight;
    458         this.scrollerHeight = this.scroller.offsetHeight;
    459         this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
    460 
    461         this.endTime = 0;
    462 
    463         this._execEvent('refresh');
    464 
    465         this.resetPosition();
    466 
    467       },
    468       _initEvents: function (remove) {
    469         var eventType = remove ? utils.removeEvent : utils.addEvent;
    470         var target = this.options.bindToWrapper ? this.wrapper : window;
    471 
    472         eventType(window, 'orientationchange', this);
    473         eventType(window, 'resize', this);
    474 
    475         if (utils.hasTouch) {
    476           eventType(this.wrapper, 'touchstart', this);
    477           eventType(target, 'touchmove', this);
    478           eventType(target, 'touchcancel', this);
    479           eventType(target, 'touchend', this);
    480         } else {
    481           eventType(this.wrapper, 'mousedown', this);
    482           eventType(target, 'mousemove', this);
    483           eventType(target, 'mousecancel', this);
    484           eventType(target, 'mouseup', this);
    485         }
    486 
    487         eventType(this.scroller, 'transitionend', this);
    488         eventType(this.scroller, 'webkitTransitionEnd', this);
    489         eventType(this.scroller, 'oTransitionEnd', this);
    490         eventType(this.scroller, 'MSTransitionEnd', this);
    491       },
    492       _start: function (e) {
    493         if (!this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated)) {
    494           return;
    495         }
    496 
    497         var point = e.touches ? e.touches[0] : e, pos;
    498         this.initiated = utils.eventType[e.type];
    499 
    500         this.moved = false;
    501 
    502         this.distY = 0;
    503 
    504         //开启动画时间,如果之前有动画的话,便要停止动画,这里因为没有传时间,所以动画便直接停止了
    505         this._transitionTime();
    506 
    507         this.startTime = utils.getTime();
    508 
    509         //如果正在进行动画,需要停止,并且触发滑动结束事件
    510         if (this.isInTransition) {
    511           this.isInTransition = false;
    512           pos = this.getComputedPosition();
    513           var _x = Math.round(pos.x);
    514           var _y = Math.round(pos.y);
    515 
    516           if (_y < 0 && _y > this.maxScrollY && this.options.adjustXY) {
    517             _y = this.options.adjustXY.call(this, _x, _y).y;
    518           }
    519 
    520           //移动过去
    521           this._translate(_x, _y);
    522           this._execEvent('scrollEnd');
    523         }
    524 
    525         this.startX = this.x;
    526         this.startY = this.y;
    527         this.absStartX = this.x;
    528         this.absStartY = this.y;
    529         this.pointX = point.pageX;
    530         this.pointY = point.pageY;
    531 
    532         this._execEvent('beforeScrollStart');
    533 
    534         e.preventDefault();
    535 
    536       },
    537 
    538       _move: function (e) {
    539         if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
    540           return;
    541         }
    542         e.preventDefault();
    543 
    544         var point = e.touches ? e.touches[0] : e,
    545       deltaX = point.pageX - this.pointX,
    546       deltaY = point.pageY - this.pointY,
    547       timestamp = utils.getTime(),
    548       newX, newY,
    549       absDistX, absDistY;
    550 
    551         this.pointX = point.pageX;
    552         this.pointY = point.pageY;
    553 
    554         this.distX += deltaX;
    555         this.distY += deltaY;
    556         absDistX = Math.abs(this.distX);
    557         absDistY = Math.abs(this.distY);
    558 
    559         // 如果一直按着没反应的话这里就直接返回了
    560         if (timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10)) {
    561           return;
    562         }
    563 
    564         newY = this.y + deltaY;
    565 
    566         if (newY > 0 || newY < this.maxScrollY) {
    567           newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
    568         }
    569 
    570         if (!this.moved) {
    571           this._execEvent('scrollStart');
    572         }
    573 
    574         this.moved = true;
    575 
    576         //暂时只考虑input问题,有效再扩展
    577         var el = document.activeElement;
    578         if (el.nodeName.toLowerCase() == 'input') {
    579           el.blur();
    580           this.disable();
    581           setTimeout($.proxy(function () {
    582             this.enable();
    583           }, this), 250);
    584           return;
    585         }
    586 
    587         this._translate(0, newY, true);
    588 
    589         if (timestamp - this.startTime > 300) {
    590           this.startTime = timestamp;
    591           this.startX = this.x;
    592           this.startY = this.y;
    593         }
    594 
    595 
    596       },
    597       _end: function (e) {
    598 
    599         if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
    600           return;
    601         }
    602 
    603         var point = e.changedTouches ? e.changedTouches[0] : e,
    604       momentumY,
    605       duration = utils.getTime() - this.startTime,
    606       newX = Math.round(this.x),
    607       newY = Math.round(this.y),
    608       distanceX = Math.abs(newX - this.startX),
    609       distanceY = Math.abs(newY - this.startY),
    610       time = 0,
    611       easing = '';
    612 
    613         this.isInTransition = 0;
    614         this.initiated = 0;
    615         this.endTime = utils.getTime();
    616 
    617         if (this.resetPosition(this.options.bounceTime)) {
    618           return;
    619         }
    620 
    621         this.scrollTo(newX, newY);
    622         if (!this.moved) {
    623           //click 的情况
    624 
    625           this._execEvent('scrollCancel');
    626           return;
    627         }
    628 
    629         if (duration < 300) {
    630 
    631           momentumY = utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0);
    632           //      newX = momentumX.destination;
    633           newY = momentumY.destination;
    634           time = Math.max(momentumY.duration);
    635           this.isInTransition = 1;
    636         }
    637 
    638         if (newY != this.y) {
    639           if (newY > 0 || newY < this.maxScrollY) {
    640             easing = utils.ease.quadratic;
    641           }
    642 
    643           this.scrollTo(newX, newY, time, easing);
    644           return;
    645         }
    646 
    647         this._execEvent('scrollEnd');
    648       },
    649 
    650       _resize: function () {
    651         var that = this;
    652 
    653         clearTimeout(this.resizeTimeout);
    654 
    655         this.resizeTimeout = setTimeout(function () {
    656           that.refresh();
    657         }, this.options.resizePolling);
    658       },
    659 
    660       _transitionTimingFunction: function (easing) {
    661         this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
    662 
    663         this.indicator && this.indicator.transitionTimingFunction(easing);
    664       },
    665 
    666       //开始或者停止动画
    667       _transitionTime: function (time) {
    668         time = time || 0;
    669         this.scrollerStyle[utils.style.transitionDuration] = time + 'ms';
    670 
    671         //滚动条,我们这里只会出现一个滚动条就不搞那么复杂了
    672         this.indicator && this.indicator.transitionTime(time);
    673 
    674       },
    675 
    676       getComputedPosition: function () {
    677         var matrix = window.getComputedStyle(this.scroller, null), x, y;
    678 
    679         matrix = matrix[utils.style.transform].split(')')[0].split(', ');
    680         x = +(matrix[12] || matrix[4]);
    681         y = +(matrix[13] || matrix[5]);
    682 
    683         return { x: x, y: y };
    684       },
    685 
    686       _initIndicator: function () {
    687         //滚动条
    688         var el = createDefaultScrollbar();
    689         this.wrapper.appendChild(el);
    690         this.indicator = new Indicator(this, { el: el });
    691 
    692         this.on('scrollEnd', function () {
    693           this.indicator.fade();
    694         });
    695 
    696         var scope = this;
    697         this.on('scrollCancel', function () {
    698           scope.indicator.fade();
    699         });
    700 
    701         this.on('scrollStart', function () {
    702           scope.indicator.fade(1);
    703         });
    704 
    705         this.on('beforeScrollStart', function () {
    706           scope.indicator.fade(1, true);
    707         });
    708 
    709         this.on('refresh', function () {
    710           scope.indicator.refresh();
    711         });
    712 
    713       },
    714 
    715       //移动x,y这里比较简单就不分离y了
    716       _translate: function (x, y, isStep) {
    717 
    718         //处理步长
    719         if (this.options.setp && !isStep) {
    720           var flag2 = y > 0 ? 1 : -1; //这个会影响后面的计算结果
    721           var top = Math.abs(y);
    722           var mod = top % this.options.setp;
    723           top = (parseInt(top / this.options.setp) * this.options.setp + (mod > (this.options.setp / 2) ? this.options.setp : 0)) * flag2;
    724           y = top;
    725         }
    726 
    727         this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
    728 
    729         this.x = x;
    730         this.y = y;
    731 
    732         if (this.options.scrollbars) {
    733           this.indicator.updatePosition();
    734         }
    735 
    736       },
    737 
    738       resetPosition: function (time) {
    739         var x = this.x,
    740         y = this.y;
    741 
    742         time = time || 0;
    743 
    744         if (this.y > 0) {
    745           y = 0;
    746         } else if (this.y < this.maxScrollY) {
    747           y = this.maxScrollY;
    748         }
    749 
    750         if (y == this.y) {
    751           return false;
    752         }
    753 
    754         this.scrollTo(x, y, time, this.options.bounceEasing);
    755 
    756         return true;
    757       },
    758 
    759       //移动
    760       scrollTo: function (x, y, time, easing) {
    761 
    762         //      //l_wang 必须项目高度的整数
    763         //      if (y < 0 && y > this.maxScrollY && this.options.adjustXY) {
    764         //        y = this.options.adjustXY.call(this, x, y).y;
    765         //      }
    766 
    767 
    768         if (this.options.adjustXY) {
    769           y = this.options.adjustXY.call(this, x, y).y;
    770         }
    771 
    772         //l_wang 验证该项是否可选
    773         if (this.options.checkSelected) {
    774           y = this.options.checkSelected.call(this, x, y).y;
    775         }
    776 
    777         easing = easing || utils.ease.circular;
    778 
    779         this.isInTransition = time > 0;
    780 
    781         if (!time || easing.style) {
    782           this._transitionTimingFunction(easing.style);
    783           this._transitionTime(time);
    784           this._translate(x, y);
    785         }
    786       },
    787 
    788       //统一的关闭接口
    789       disable: function () {
    790         this.enabled = false;
    791       },
    792       //统一的open接口
    793       enable: function () {
    794         this.enabled = true;
    795       },
    796 
    797       on: function (type, fn) {
    798         if (!this._events[type]) {
    799           this._events[type] = [];
    800         }
    801 
    802         this._events[type].push(fn);
    803       },
    804 
    805       _execEvent: function (type) {
    806         if (!this._events[type]) {
    807           return;
    808         }
    809 
    810         var i = 0,
    811             l = this._events[type].length;
    812 
    813         if (!l) {
    814           return;
    815         }
    816 
    817         for (; i < l; i++) {
    818           this._events[type][i].call(this);
    819         }
    820       },
    821       destroy: function () {
    822         this._initEvents(true);
    823         this._execEvent('destroy');
    824         this.indicator && this.indicator.destroy();
    825 
    826         console.log('destroy')
    827 
    828       },
    829 
    830       _transitionEnd: function (e) {
    831         if (e.target != this.scroller || !this.isInTransition) {
    832           return;
    833         }
    834 
    835         this._transitionTime();
    836         if (!this.resetPosition(this.options.bounceTime)) {
    837           this.isInTransition = false;
    838           this._execEvent('scrollEnd');
    839         }
    840       },
    841 
    842       //事件具体触发点
    843       handleEvent: function (e) {
    844         switch (e.type) {
    845           case 'touchstart':
    846           case 'mousedown':
    847             this._start(e);
    848             break;
    849           case 'touchmove':
    850           case 'mousemove':
    851             this._move(e);
    852             break;
    853           case 'touchend':
    854           case 'mouseup':
    855           case 'touchcancel':
    856           case 'mousecancel':
    857             this._end(e);
    858             break;
    859           case 'orientationchange':
    860           case 'resize':
    861             this._resize();
    862             break;
    863           case 'transitionend':
    864           case 'webkitTransitionEnd':
    865           case 'oTransitionEnd':
    866           case 'MSTransitionEnd':
    867             this._transitionEnd(e);
    868             break;
    869         }
    870       }
    871 
    872     };
    873 
    874     function createDefaultScrollbar() {
    875       var scrollbar = document.createElement('div'),
    876         indicator = document.createElement('div');
    877 
    878       scrollbar.style.cssText = 'position:absolute;z-index:9999';
    879       scrollbar.style.cssText += ';7px;bottom:2px;top:2px;right:1px';
    880       scrollbar.style.cssText += ';overflow:hidden';
    881 
    882       indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px';
    883       indicator.style.width = '100%';
    884 
    885       scrollbar.appendChild(indicator);
    886 
    887       return scrollbar;
    888     }
    889 
    890     function Indicator(scroller, opts) {
    891       this.wrapper = typeof opts.el == 'string' ? document.querySelector(opts.el) : opts.el;
    892       this.indicator = this.wrapper.children[0];
    893 
    894       this.wrapperStyle = this.wrapper.style;
    895       this.indicatorStyle = this.indicator.style;
    896       this.scroller = scroller;
    897 
    898       this.sizeRatioY = 1;
    899       this.maxPosY = 0;
    900 
    901       this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
    902       this.wrapperStyle[utils.style.transitionDuration] = '0ms';
    903       //this.wrapperStyle.opacity = '0';
    904     }
    905 
    906     Indicator.prototype = {
    907       transitionTime: function (time) {
    908         time = time || 0;
    909         this.indicatorStyle[utils.style.transitionDuration] = time + 'ms';
    910       },
    911       transitionTimingFunction: function (easing) {
    912         this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
    913       },
    914       refresh: function () {
    915 
    916         this.transitionTime();
    917 
    918         var r = this.wrapper.offsetHeight; // force refresh
    919 
    920         this.wrapperHeight = this.wrapper.clientHeight;
    921 
    922 
    923         this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
    924         this.indicatorStyle.height = this.indicatorHeight + 'px';
    925 
    926 
    927         this.maxPosY = this.wrapperHeight - this.indicatorHeight;
    928         this.sizeRatioY = (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
    929 
    930         this.updatePosition();
    931       },
    932       destroy: function () {
    933         this.wrapper.remove();
    934       },
    935       updatePosition: function () {
    936         var y = Math.round(this.sizeRatioY * this.scroller.y) || 0;
    937         this.y = y;
    938 
    939         //不需要兼容方式了
    940         this.indicatorStyle[utils.style.transform] = 'translate(0px,' + y + 'px)' + this.scroller.translateZ;
    941 
    942       },
    943       fade: function (val, hold) {
    944         if (hold && !this.visible) {
    945           return;
    946         }
    947 
    948         clearTimeout(this.fadeTimeout);
    949         this.fadeTimeout = null;
    950 
    951         var time = val ? 250 : 500,
    952             delay = val ? 0 : 300;
    953 
    954         val = val ? '1' : '0';
    955 
    956         this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
    957 
    958         this.fadeTimeout = setTimeout($.proxy(function (val) {
    959           this.wrapperStyle.opacity = val;
    960           this.visible = +val;
    961         }, this), delay);
    962 
    963       }
    964     };
    965 
    966     IScroll.utils = utils;
    967 
    968   </script>
    969   <script type="text/javascript">
    970 
    971     var s = new IScroll({
    972       wrapper: $('#wrapper'),
    973       scroller: $('#scroller'),
    974       scrollbars: false,
    975       setp: 30
    976     });
    977 
    978     var s1 = new IScroll({
    979       wrapper: $('#wrapper2'),
    980       scroller: $('#scroller2'),
    981       scrollbars: false,
    982       setp: 30
    983     });
    984 
    985     new FastClick(document.body);
    986 
    987   </script>
    988 </body>
    989 </html>
    View Code

    这里,我们将滑动事件绑定到了各个wrapper上,所以不会出现卡的现象,以后各位自己要注意:

     

    异步DOM加载,不可滑动

    这个问题其实比较简单,只需要每次操作后执行一次refresh,方法即可,这里重启一行有点坑爹了 

    大杀器

    往往最后介绍的方法最为牛B,不错,小钗还有一招大杀器可以解决以上问题,

    http://sandbox.runjs.cn/show/s3dqvlfk

     1 _start: function (e) {
     2   if (!this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated)) {
     3     return;
     4   }
     5 
     6 
     7   //暂时只考虑input问题,有效再扩展
     8   var el = document.activeElement;
     9   if (el.nodeName.toLowerCase() == 'input') {
    10     return;
    11   }
    12 
    13 
    14   var point = e.touches ? e.touches[0] : e, pos;
    15   this.initiated = utils.eventType[e.type];
    16 
    17   this.moved = false;
    18 
    19   this.distY = 0;
    20 
    21   //开启动画时间,如果之前有动画的话,便要停止动画,这里因为没有传时间,所以动画便直接停止了
    22   this._transitionTime();
    23 
    24   this.startTime = utils.getTime();
    25 
    26   //如果正在进行动画,需要停止,并且触发滑动结束事件
    27   if (this.isInTransition) {
    28     this.isInTransition = false;
    29     pos = this.getComputedPosition();
    30     var _x = Math.round(pos.x);
    31     var _y = Math.round(pos.y);
    32 
    33     if (_y < 0 && _y > this.maxScrollY && this.options.adjustXY) {
    34       _y = this.options.adjustXY.call(this, _x, _y).y;
    35     }
    36 
    37     //移动过去
    38     this._translate(_x, _y);
    39     this._execEvent('scrollEnd');
    40   }
    41 
    42   this.startX = this.x;
    43   this.startY = this.y;
    44   this.absStartX = this.x;
    45   this.absStartY = this.y;
    46   this.pointX = point.pageX;
    47   this.pointY = point.pageY;
    48 
    49   this._execEvent('beforeScrollStart');
    50 
    51   e.preventDefault();
    52 
    53 },

    每次touchStart的时候若是发现当前获得焦点的是input,便不予理睬了,这个时候滑动效果是系统滑动,各位可以一试

    结语

    关于IScroll的研究暂时告一段落,希望此文对各位有帮助,经过这次的深入学习同时也对小钗的一些问题得到了处理

    我相信将之用于项目重的点会越来越多!

    若是各位在实际工作中遇到什么IScroll的疑难杂症可以留言,有时间小钗愿意整理解决方案

  • 相关阅读:
    iOS NSString中的搜索方法rangeOfString
    iOS 远程推送通知
    iOS PushMeBaby日志提示SSLwrite():-36 94
    iOS [[NSBundle mainBundle] pathForResource:@"" ofType:@""]无法获取到文件
    iOS 申请测试用的远程推送证书
    数据结构与算法学习笔记(五)
    iOS 上传新版本到AppStore时报错ITMS-90034
    数据结构与算法学习笔记(四)
    数据结构与算法学习笔记(三)
    iOS开发日记49-详解定位CLLocation
  • 原文地址:https://www.cnblogs.com/yexiaochai/p/3764503.html
Copyright © 2011-2022 走看看