【IScroll深入学习】解决IScroll疑难杂症
前言
在去年,我们对IScroll的源码进行了学习,并且分离出了一段代码自己使用,在使用学习过程中发现几个致命问题:
① 光标移位
② 文本框找不到(先让文本框获取焦点,再滑动一下,输入文字便可重现)
③ 偶尔导致头部消失,头部可不是fixed哦
由于以上问题,加之去年我们团队的工作量极大,和中间一些组织架构调整,这个事情一直被放到了今天,心里一直对此耿耿于怀,因为IScroll让人忘不了的好处
小钗坚信,IScroll可以带来前端体验上的革命,因为他可以解决以下问题
- 区域滑动顺滑感的体验
- 解决fixed移位问题
- 解决动画过程中长短页的问题,并且可以优化view切换动画的顺畅度
我们不能因为一两个小问题而放弃如此牛逼的点子,所以我们要处理其中的问题,那么这些问题是否真的不可解决,而引起这些问题的原因又到底是什么,我们今天来一一探索
PS:该问题已有更好的解决方案,待续
抽离IScroll
第一步依旧是抽离IScroll核心逻辑,我们这里先在简单层面上探究问题,以免被旁枝末节的BUG困扰,这里形成的一个库只支持纵向滚动,代码量比较少
代码中引入了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="width: 100px; position: relative; "> 204 <ul class="ul-list" id="scroller" style="left: 40%; width: 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="width: 100px; position: relative;"> 219 <ul class="ul-list" id="scroller2" style="right: 10%; width: 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 </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 += ';width: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>
这里,我们将滑动事件绑定到了各个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的疑难杂症可以留言,有时间小钗愿意整理解决方案