1 mvvm 学习
1.1 实现原理
mvvm 类框架的实现原理不复杂,大致如下:
- 模板分析得到依赖的属性
- 通过某种变动监测手段监测这些依赖的属性
- 当属性变动的时候,触发相应的 directive 的处理逻辑即可
实际上,directive 的处理逻辑不一定是对 view 进行操作,比如上报。但是,在 mv 的思想下,建议对 view 的操作都集中在 directive 里实现
1. iconfont 简介
什么是 iconfont?正如字面意思,就是图标字体,下面我给大家慢慢道来
web 页面包含什么元素?
- 文字
- 链接
- 流媒体
- 视频
- 音频
- 图片
- 背景图(大)
- 插图(中,例如照片集,课程封面等)
- 图标(小)
在所有包含 ui 的程序架构中,以上不同的 ui 元素在各种环境中都会遭遇到不同的问题,同时也都有与之对应的解决方案
iconfont 就是一个解决【图标】问题的解决方案之一
1.1 图标问题
首先来看一下图标会有什么难题:
- 图标的大小会变,在两种情况下:
- 每次 ui 改版,那些厌倦一成不变喜欢尝鲜的设计师们总是会淘气地改变图标大小,图标一般是点缀其他事物,例如文字,有时候设计会把某个图标从一段描述文字改成标题的点缀,这时候图标就需要变大
- 自适应页面,整个页面的大小都在变,难道图标还能独善其身吗?
- 图标会经常换,这点还是那些设计师的问题,他们说不好看要换,作为开发我们还能说什么?
- 图标自身也会变,“来把那个图标改成红色吧,鲜艳点!”,“那个谁,把那个图标的底色改成蓝色吧~”,“…”
背景图和插图一般不会有这些问题,他们偏向于主体内容而不是点缀,一般不会改变。背景图一般是平铺,插图一般也是定宽,所以大小也不怎么需要改变。
1.2 图标解决方案
图标的解决方案有:
- 使用 png 图片,这是最传统的方案,兼容性最好,使用方面基本不会有技术问题。但是它不够方便,主要有以下两个问题:
- 只要图标稍有改动都必须换新的图片,即使只是换个颜色或者透明度
- 图标大小改变问题,不管是改图标还是需要自适应。使用大图片会造成两个问题:缩放效率和大图流量浪费加载慢
- 使用 svg 图片可以很好的解决图片大小的问题,但是兼容性是这个解决方案的致命问题,svg 不兼容 IE6~8,svg 不兼容 Android2.3
- css,对于一些简单的图标,比如箭头,叉叉等,可以使用 css 来绘制而成,这也是现在移动端经常使用的方式,毕竟移动端对 css3 兼容性较好,但是利用 css 绘制的图片的问题是不能支持自适应,自适应的 css 会出现小数点,图标本身就是小的,所以当位置相差 1px 看起来都会很明显
1.3 iconfont 解决方案
iconfont 是图标的另一种解决方案,它是把一些简单的图标制作成字体,然后让图标变成和字体一样使用
iconfont 有以下优点:
- 字体是矢量的,所以可以随意改变大小
- 因为它是字体,所以所有字体的 css 都可以使用,比如 font-size,color,background,opacity 等
- iconfont 的制作也很简单,现在有很多线上制作 iconfont 的站点,只要上传 svg 的图标设计稿,就能线上生成 iconfont 字体文件,而且连使用代码都直接生成
- iconfont 没有兼容性问题,IE6,Android2.3 都能够兼容
2. 使用 iconfont
虽然使用 iconfont 没有图片那么简单,但也没有想象中那么难,下面来看看怎么使用 iconfont
首先,你需要制作 iconfont,现在有许多 iconfont 的站点,比如 fontello
我们随便选择一个图标:
然后我们可以自定义字码:
最后我们就可以下载字体文件了:
下载完,解压出来,我们可以看到有 demo,有字体文件,也有使用代码:
可以看到使用代码里面已经把各种兼容性考虑到了:
使用代码也给到,注意使用类名也是可以在站点中自定义的:
另外注意的是,字体文件也是可以内链的,在 fontello-embedded.css 文件里面:
基本上,利用提供的代码,基本我们就可以完全兼容的使用 iconfont,下面介绍移动端使用 iconfont,在移动端只需要如下代码:
在移动端,只需要 truetype 类型
效果如下:
使用 iconfont,我们可以应用许多字体样式,现在我们改变一下样式:
效果如下:
可以发现,改变一个图标的颜色,背景色,大小都是非常方便的一件事
另一方面,当需要改一个图标的时候,我们可以在制作 iconfont 的时候,替换掉一个图标即可,使用的类名和字码都是可以定制的,这样就可以在不用修改业务使用代码的情况下,只需要替换掉内链的字体代码就可以完成替换图标的工作
以上代码经过实机测试,兼容 IOS4,Android2.3
在移动端,iconfont 也可以使用外链形式,这里就不再赘述
3. 小结
在解决图标的问题上,不管是兼容性,灵活性,扩展性,iconfont 都是一个很好的解决方案
3.1 iconfont 优势
- 灵活性,改变图标的颜色,背景色,大小都非常简单
- 兼容性,兼容所有流行的浏览器,不仅 h5 可以使用 iconfont,app 也可以使用 iconfont,关于这方面可以查看其它线上分享
- 扩展性,替换图标很方便,新增图标也非常简单,也不需要考虑图标合并的问题,图片方案需要 css sprite
- 高效性,iconfont 有矢量特性,没有图片缩放的消耗高
- 在使用上字体文件和普通的静态资源一样,既可以外链也可以内链,并且字体文件也可以使用 gzip 压缩
- 在移动端上,可以只使用 truetype 类型,非常灵活小巧
- 现在很多项目已经在使用 iconfont,先不说国外,比如国内,阿里巴巴各个平台(不仅 pc,h5,还有 app)已经全面使用 iconfont,并且阿里巴巴还搭建了一个线上 iconfont 站点,这是一个很完善的站点,上面有阿里几个主要业务的图标资源库,也可以让用户自己制作图标,完善用户自身的图标库,让用户之间可以共享形成生态,同时站点的使用说明也非常完整,从图标设计,iconfont 制作和 iconfont 使用(里面包含了各个平台的使用方法)都有很完善的说明
3.2 iconfont 缺点
- 制作 iconfont 需要 svg 设计稿,对于开发来说没有图片来的方便
- iconfont 有些特有的问题,详情可参考 @font-face and performance,不过许多问题在移动端是不存在的
3.3 结语
总的来说,iconfont 是可以应用的,特别是在移动端,如果不兼容 Android2.3,使用 svg 图片也是可以接受的,实际上制作 iconfont 也是需要 svg 资源的,所以两者其实很类似
另外,阿里巴巴主要业务都已经广泛应用 iconfont,并且还有成熟的线上站点支持,最起码在可行性方面是可以不用过多考虑的,虽然在使用 iconfont 的过程中可能会遇到许多问题,但是鉴于 iconfont 应用广泛的前提下,线上的资源也会非常丰富,应该不需要过多的担心
最后想说,我们业务是可以考虑使用的!如果要应用 iconfont,我们还需要设计们的支持!
这两个伪元素分别表示元素内容的【前】【后】,利用这两个伪元素可以在元素内容的前后添加内容,其实这没有什么前后的概念,如果应用了 absolute 的特性之后,你可以把这些伪元素放在任何位置,有了这两个伪元素,就代表每个元素都有两个助手可供使用,灵活运用它们的话将会得到很多有趣的实现,简化许多实现。
伪元素特性(目前已经遇到的)
- 它不存在于文档中,所以 js 无法操作它
- 它属于主元素本身,有些伪类仅仅是代表元素内容的一部分,譬如:first-letter 代表第一个字母;因此当伪元素被点击的时候触发的是主元素的 click 事件
- 块级元素才能有:before, :after,譬如 img 就不能设置,亦即某些元素是没有:before, :after 的,只要知道一般的块级元素都可以用就行了
注:css3 中,为了与伪类区分,伪元素前应该使用两个冒号,即:hover 伪类,::before 伪元素。当然为了向下兼容,用一个冒号也是可以的,不过建议尽量使用规范的写法。
各种图标
利用这两个伪类,可以实现需要简单的图标,例如搜索的放大镜,叉叉,箭头等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<span class="comment">//视频播放图标</span> .course[data-type=<span class="string">"2"</span>] { .course__cover { &:before, &:after { content: <span class="string">''</span>; display: block; position: absolute; left: <span class="number">5</span>px; bottom: <span class="number">5</span>px; } &:after { <span class="comment">//实现圈圈</span> width: <span class="number">20</span>px; height: <span class="number">20</span>px; border: <span class="number">2</span>px solid white; background: rgba(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, .<span class="number">6</span>); border-radius: <span class="number">12</span>px; background-clip: padding-box; } &:before { <span class="comment">//实现三角形</span> height: <span class="number">0</span>; width: <span class="number">0</span>; border-left: <span class="number">8</span>px solid white; border-top: <span class="number">5</span>px solid transparent; border-bottom: <span class="number">5</span>px solid transparent; margin-left: <span class="number">9</span>px; margin-bottom: <span class="number">7</span>px; z-index: <span class="number">5</span>; } } } |
上述图标的效果如下图:
扩大可点区域
在 mobile,特别是小屏手机,可点区域一般需要大一点,这样对用户友好一点。
当主元素实在没办法扩大自身的时候,可以利用:before, :after 来实现可点区域的扩大,还记得伪元素的特性之一,伪元素属于主元素,点伪元素就是点击主元素。
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="comment">//利用这个样式可以把可点区域扩大为40px宽,高度原理一样</span> &:before { content: <span class="string">""</span>; display: block; position: absolute; width: <span class="number">40</span>px; left: <span class="number">50</span>%; margin-left: -<span class="number">20</span>px; top: <span class="number">0</span>; height: <span class="number">50</span>px; <span class="comment">//随便</span> } |
实现效果如下图:
这个算是个综合例子,应用了多种技巧。这是一个收藏按钮,两种状态:已收藏和未收藏,正好符合 checkbox,因此应用了 checkbox,修改了样式给了一个底图。然后下面的文字是:after,利用了动态 label 的技巧(详见 1.4),然后:before 应用了扩大可点区域技巧,使得 40*50 的区域内点击都有效。
实现 label
对于一些静态的文字,说明性的文字,譬如最常见的上图下字单元,完全可以用:after 实现那个文字。
还记得 form 家族的 label 标签吗?它的特性是 label 和 input 的联动,点击 label 元素就等同于点击 input,这个效果和伪元素的特性是一模一样的,用:before 完全可以替代 label。
唯有一点是 label 独有的,就是 label 元素和 input 元素的位置相距较远,这种联动是伪元素无法实现的,毕竟伪元素还是要围绕主元素存在的,远距离 absolute 将会引发灾难的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
.empty__bg { display: inline-block; width: <span class="number">95</span>px; height: <span class="number">92</span>px; background: url(../../img/bg_empty_center@<span class="number">2</span>x.png) no-repeat; background-size: <span class="number">95</span>px <span class="number">92</span>px; position: relative; margin-bottom: <span class="number">16</span>px; <span class="comment">//注意这里需要留好位置放置after元素(它是absolute进去的)</span> &:after { content: <span class="string">"暂无学习计划"</span>; display: block; font-size: <span class="number">14</span>px; line-height: <span class="number">24</span>px; text-align: center; width: <span class="number">100</span>%; color: <span class="comment">#909090;</span> position: absolute; top: <span class="number">100</span>%; left: <span class="number">0</span>; } } |
实现效果如下图:
实现动态信息
如果你认为伪元素只能实现静态 label,那就把 CSS3 想得简单了。
:before, :after 的 content 属性的值除了是静态字符串之外,还有其他的一些特殊值,其中一个是 attr(…),这个特性的作用是用主元素的某个属性的值作为 content 的值,当这个属性的值改变的时候,伪元素的值也会跟着改变,利用这个特性就可以实现动态信息了。
可以有两种动态方案:
- 用 js 动态修改主元素的属性值,这个很直观
- 修改伪元素 content 关联的属性
下面看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span class="comment">//html:</span> <span class="comment">//<a class="datepicker__link z-today" href="javascript:void(0)" data-monthstr="04月" data-weekstr="周三" data-k="2015422"><span>22</span></a></span> <span class="comment">//<li class="datepicker__item"><a class="datepicker__link" href="javascript:void(0)" data-monthstr="04月" data-weekstr="周四" data-k="2015423"><span>23</span></a></li></span> <span class="comment">//<a class="datepicker__link z-active" href="javascript:void(0)" data-monthstr="04月" data-weekstr="周五" data-k="2015424"><span>24</span></a></span> <span class="comment">//core css</span> .datepicker__link { &:before { content: attr(data-monthStr); } &.z-active { &:before { content: attr(data-weekStr); } } } |
实现的效果如下图:
这里用到第二种动态方案,日期列表里面,日期上面默认显示月份信息,但是选中态需要显示星期信息。
预先把每个单元用到的月份信息和星期信息放到主元素的 data 属性上面(缓存的思想),选中的时候一般都是要添加一个选中态样式,这时,除了基本的凸显性样式外,同时切换伪元素关联的 data 属性即可轻松地解决这个问题,而不用通过 js 去找到 label 元素,然后修改 text。
小结
伪元素帮助我们选择那些有特殊意义,但是却无法具体定位的 “东西”,它们对这些 “东西” 不做任何限制,它们只是代表这些特殊意义,譬如:first-letter,不限制首字母是哪一个字母,只是代表了首字母元素。
通过伪元素,可以让 CSS 更好的处理一些有特殊意义的元素,这些元素一般很难定位,甚至有些是代表状态的元素,譬如:target。
结合伪元素的特点,利用它们的灵活性,可以为我们提供更多的特性,下面总结一下目前想到的伪元素的优缺点:
优点/用途
- 减少 dom 节点数
- 让 css 帮助解决一部分 js 问题,让问题变得简单
缺点
- 不利于 SEO
- 代码读起来 “可能” 会有疑惑
前言
最近项目需要实现一个 fixed 标题栏的功能,很普通的功能,实现核心也是在 sroll 事件中切换到 fixed 状态即可,但是在某些版本 ios 的某些内核中,在惯性滚动过程中不执行任何 js 代码,亦即不会触发 scroll 事件,基本任何事情都做不了,为了解决这个问题不得不使用 div 内滚动,然后使用 iscroll 库实现滚动逻辑。
基于使用过程中的一些问题,抱着学习的态度,稍微看了一下源代码,现把学习所得记录如下。
源代码学习
核心实现
滑动相关组件(如 swipe 库)的实现基本都是类似的,就是通过 3 个核心事件:touchstart,touchmove,touchend 完成操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span class="keyword">switch</span> ( e.type ) { <span class="keyword">case</span> <span class="string">'touchstart'</span>: <span class="keyword">case</span> <span class="string">'mousedown'</span>: <span class="keyword">this</span>._start(e); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">'touchmove'</span>: <span class="keyword">case</span> <span class="string">'mousemove'</span>: <span class="keyword">this</span>._move(e); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">'touchend'</span>: <span class="keyword">case</span> <span class="string">'mouseup'</span>: <span class="keyword">this</span>._end(e); <span class="keyword">break</span>; } |
注:下面的源码只罗列核心部分,而且只展示 y 轴方向
touchstart 需要做的事情有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<span class="keyword">function</span> _start(e) { <span class="keyword">var</span> point = e.touches ? e.touches[<span class="number">0</span>] : e; <span class="comment">//[1]</span> <span class="comment">//初始化相关数据,一般是开始滑动的位置基点,时间基点</span> <span class="comment">//还有相关的变量</span> <span class="keyword">this</span>.moved = <span class="keyword">false</span>; <span class="keyword">this</span>.distY = <span class="number">0</span>; <span class="keyword">this</span>.directionY = <span class="number">0</span>; <span class="keyword">this</span>.startTime = utils.getTime(); <span class="keyword">this</span>.startY = <span class="keyword">this</span>.y; <span class="keyword">this</span>.pointY = point.pageY; <span class="comment">//[2]</span> <span class="comment">//如果正在滑动中,需要对此做处理,一般策略有:</span> <span class="comment">//1. 在当前滑动状态的基础上,叠加新的滑动状态</span> <span class="comment">//2. 立刻停止当前的滑动,开始新的滑动</span> <span class="comment">//iscroll使用的是方案2</span> <span class="comment">//方案1对于状态处理,滑速计算等方面略偏复杂,但这是更加合理的处理策略(原生的scroll也是这样的)</span> <span class="comment">//这有点类似开车时踩油门的场景,想象一下就清楚了。。。</span> <span class="keyword">if</span> ( !<span class="keyword">this</span>.options.useTransition && <span class="keyword">this</span>.isAnimating ) { <span class="keyword">this</span>.isAnimating = <span class="keyword">false</span>; <span class="keyword">this</span>._execEvent(<span class="string">'scrollEnd'</span>); } } |
touchmove 需要做的事情有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<span class="keyword">function</span> _move(e) { <span class="comment">//[1]</span> <span class="comment">//计算位置和时间,各种增量</span> <span class="keyword">var</span> point = e.touches ? e.touches[<span class="number">0</span>] : e, deltaY = point.pageY - <span class="keyword">this</span>.pointY, timestamp = utils.getTime(), newY, absDistY; <span class="keyword">this</span>.pointY = point.pageY; <span class="keyword">this</span>.distY += deltaY; absDistY = Math.abs(<span class="keyword">this</span>.distY); <span class="comment">//[2]</span> <span class="comment">//判定是否是标准滑动,防止手抖干扰</span> <span class="comment">//干扰有时候是很大的,特别是有惯性滑动逻辑的时候就更甚了,所以这个细节是少不了的</span> <span class="keyword">if</span> ( timestamp - <span class="keyword">this</span>.endTime > <span class="number">300</span> && (absDistX < <span class="number">10</span> && absDistY < <span class="number">10</span>) ) { <span class="keyword">return</span>; } newY = <span class="keyword">this</span>.y + deltaY; <span class="comment">//[3]</span> <span class="comment">//判断滑动是否超出范围了</span> <span class="comment">//自从ios出现了负向滚动效果之后,各种滑动组件都跟着实现了这种bounce效果</span> <span class="keyword">if</span> ( newY > <span class="number">0</span> || newY < <span class="keyword">this</span>.maxScrollY ) { newY = <span class="keyword">this</span>.options.bounce ? <span class="keyword">this</span>.y + deltaY / <span class="number">3</span> : newY > <span class="number">0</span> ? <span class="number">0</span> : <span class="keyword">this</span>.maxScrollY; } <span class="comment">//[4]</span> <span class="comment">//触发scrollStart事件</span> <span class="comment">//一个健全的组件肯定有相关的插口,一般都是用事件机制实现的</span> <span class="comment">//这里的细节是,开始事件是要在判定为标准滑动才会触发的,并且只触发一次</span> <span class="comment">//如果考虑不细的话,很容易会在touchstart事件中触发事件</span> <span class="keyword">if</span> ( !<span class="keyword">this</span>.moved ) { <span class="keyword">this</span>._execEvent(<span class="string">'scrollStart'</span>); } <span class="keyword">this</span>.moved = <span class="keyword">true</span>; <span class="comment">//[5]</span> <span class="comment">//万事俱备,让页面(元素)滑过去吧!</span> <span class="keyword">this</span>._translate(newX, newY); } |
touchend 需要做的事情有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<span class="keyword">function</span> _end(e) { <span class="comment">//[1]</span> <span class="comment">//进行必要的计算</span> <span class="keyword">var</span> duration = utils.getTime() - <span class="keyword">this</span>.startTime, newY = Math.round(<span class="keyword">this</span>.y), distanceY = Math.abs(newY - <span class="keyword">this</span>.startY); <span class="keyword">this</span>.endTime = utils.getTime(); <span class="comment">//[2]</span> <span class="comment">//最后的位置也要滑过去</span> <span class="keyword">this</span>.scrollTo(newX, newY); <span class="comment">// ensures that the last position is rounded</span> <span class="comment">//[3]</span> <span class="comment">//实现惯性滑动</span> <span class="keyword">if</span> ( <span class="keyword">this</span>.options.momentum && duration < <span class="number">300</span> ) { momentumY = <span class="keyword">this</span>.hasVerticalScroll ? utils.momentum(<span class="keyword">this</span>.y, <span class="keyword">this</span>.startY, duration, <span class="keyword">this</span>.maxScrollY, <span class="keyword">this</span>.options.bounce ? <span class="keyword">this</span>.wrapperHeight : <span class="number">0</span>, <span class="keyword">this</span>.options.deceleration) : { destination: newY, duration: <span class="number">0</span> }; newY = momentumY.destination; time = Math.max(momentumX.duration, momentumY.duration); <span class="keyword">this</span>.isInTransition = <span class="number">1</span>; } <span class="keyword">if</span> ( newX != <span class="keyword">this</span>.x || newY != <span class="keyword">this</span>.y ) { <span class="keyword">this</span>.scrollTo(newX, newY, time, easing); <span class="keyword">return</span>; } <span class="comment">//[4]</span> <span class="comment">//触发滑动结束事件</span> <span class="keyword">this</span>._execEvent(<span class="string">'scrollEnd'</span>); } |
基本所有滑动相关的组件所做的事情都是这些,都可以借鉴一二的。
特殊 css prefix 缓存
用 js 处理特殊 css 的时候,可以先缓存 prefix,这样就不用每次都操作所有的内置属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<span class="keyword">var</span> _elementStyle = document.createElement(<span class="string">'div'</span>).style; <span class="keyword">var</span> _vendor = (<span class="keyword">function</span> () { <span class="keyword">var</span> vendors = [<span class="string">'t'</span>, <span class="string">'webkitT'</span>, <span class="string">'MozT'</span>, <span class="string">'msT'</span>, <span class="string">'OT'</span>], transform, i = <span class="number">0</span>, l = vendors.length; <span class="keyword">for</span> ( ; i < l; i++ ) { transform = vendors[i] + <span class="string">'ransform'</span>; <span class="keyword">if</span> ( transform in _elementStyle ) <span class="keyword">return</span> vendors[i].substr(<span class="number">0</span>, vendors[i].length-<span class="number">1</span>); } <span class="keyword">return</span> <span class="keyword">false</span>; })(); <span class="keyword">function</span> _prefixStyle (style) { <span class="keyword">if</span> ( _vendor === <span class="keyword">false</span> ) <span class="keyword">return</span> <span class="keyword">false</span>; <span class="keyword">if</span> ( _vendor === <span class="string">''</span> ) <span class="keyword">return</span> style; <span class="keyword">return</span> _vendor + style.charAt(<span class="number">0</span>).toUpperCase() + style.substr(<span class="number">1</span>); } |
事件绑定
addEventListener 绑定事件可以传入一个对象而不是一个 cb 函数,事件触发的时候,就会调用该对象的 handleEvent 方法来处理事件。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span class="keyword">var</span> event = { handleEvent: <span class="keyword">function</span>(e) { <span class="keyword">switch</span> ( e.type ) { <span class="keyword">case</span> <span class="string">'touchstart'</span>: <span class="keyword">this</span>._start(e); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">'touchmove'</span>: <span class="keyword">this</span>._move(e); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">'touchend'</span>: <span class="keyword">this</span>._end(e); <span class="keyword">break</span>; } }, _start: <span class="keyword">function</span>() {}, _move: <span class="keyword">function</span>() {}, _end: <span class="keyword">function</span>() {} } el.addEventListener(<span class="string">'touchstart'</span>, event); el.addEventListener(<span class="string">'touchmove'</span>, event); el.addEventListener(<span class="string">'touchend'</span>, event); |
这种绑定方式的优点有:
- 删除事件方便
- 事件集中处理
- 程序结构清晰
还记得那种绑定事件时 bind(this) 的日子吗。。。
这种方式也方便了实现事件代理
事件触发频率调整
对于一些触发频率较高的事件,我们通常会控制一下事件处理的频率,例如 scroll,resize 事件。
另一方面,在实现一个公共组件的时候可以考虑从组件本身来解决这个问题,iScroll 通过配置来设置 scroll 事件的触发频率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span class="comment">//下面代码在_move方法里</span> <span class="comment">//probeType == 1 则300ms才会触发一次scroll</span> <span class="keyword">if</span> ( timestamp - <span class="keyword">this</span>.startTime > <span class="number">300</span> ) { <span class="keyword">this</span>.startTime = timestamp; <span class="keyword">if</span> ( <span class="keyword">this</span>.options.probeType == <span class="number">1</span> ) { <span class="keyword">this</span>._execEvent(<span class="string">'scroll'</span>); } } <span class="comment">//probeType > 1 则一直触发</span> <span class="keyword">if</span> ( <span class="keyword">this</span>.options.probeType > <span class="number">1</span> ) { <span class="keyword">this</span>._execEvent(<span class="string">'scroll'</span>); } |
缺点与使用问题
下面是针对版本 5.1.3 的 iscroll 使用过程中的一些问题
1. 没有插件版
iScroll 没有 zepto/jquery 插件版本,一些基础方法都需要自己实现,导致了库的体积偏大。
2. 没有暴露停止滑动(惯性滑动)的接口
通过查看源代码找到了停止滑动的方法,如下:
1 2 3 4 5 |
<span class="keyword">var</span> iScroll = <span class="keyword">new</span> IScroll({ <span class="comment">/* ... */</span> }); <span class="comment">//直接通过修改iScroll对象的状态来停止滑动</span> <span class="comment">//通过这种方式停止动画是不会触发scrollEnd事件的!</span> iScroll.isAnimating = <span class="keyword">false</span> |
3. 调用 scrollTo 方法不会触发 scroll 事件
可以通过 scrollTo 方法来手动滑动,但是这样的滑动过程是不会触发 scroll 事件的。
总结
在使用 iScroll 的过程中遇到不少坑,但使用起来还是比较容易的,文档也比较齐全。
iScroll 在实现上也非常成熟,里面许多实现细节都是值得学习的