Open aooy opened 7 years ago
赞一个
赞
赞一个
有待改进的几点:
if(ic.store.preventClicks) ic.addEvent(container, 'click', ic.preventClicks, false); 这里应该设置为true,在事件捕获阶段就禁止事件冒泡的发生。 否则如果container的事件注册发生在iceSkating()之前,preventClicks会不起作用
代码整体不错,多多交流啦 :)
@julyL 看得非常仔细,谢谢指正。对于第三点,false确实不妥,true应该说能禁止大部分情况(冒泡阶段的事件和部分捕获阶段的事件),除了在iceSkating()之前给容器注册的捕获事件。
赞一个
你如果transition时间设置成0,岂不是动画瞬间执行完毕?会闪烁不顺畅的吧?
作者:杨敬卓
转载请注明出处
目录
前言
移动端,滑动是很常见的需求。很多同学都用过swiper.js,本文从原理出发,实践出一个类swiper的滑动小插件ice-skating。
小插件的例子:
在写代码的过程中产生的一些思考:
基本原理
滑动就是用
transform: translate(x,y)
或者transform: translate3d(x,y,z)
去控制元素的移动,在松手的时候判定元素最后的位置,元素的样式应用transform: translate3d(endx , endy, 0)
和transition-duration: time
来达到一个动画恢复的效果。标准浏览器提供transitionend
事件监听动画结束,在结束时将动画时间归零。Note: 这里不讨论非标准浏览器的实现,对于不支持
transform
和transition
的浏览器,可以使用position: absolute
配合left
和top
进行移动,然后用基于时间的动画的算法来模拟动画效果。html结构
举例一个基本的结构:
transform: translate3d(x,y,z)
就是应用在className为ice-slide
的元素上。这里不展示css代码,可以在ice-skating的example
文件中里查看完整的css。css代码并不是唯一的,简单说只要实现下图的结构就可以。从图中可以直观的看出,移动的是绿色的元素。className为
ice-slide
的元素的宽乘于当前索引(offsetWidth index),就是每次稳定时的偏移量。例如最开始`transform: translate3d(offsetWidth 0, 0, 0),切换到slide2后,
transform: translate3d(offsetWidth * 1, 0, 0)`,大致就是这样的过程。实践
源码位于ice-skating的
dist/iceSkating.js
。我给插件起名叫ice-skating
,希望它像在冰面一样顺畅^_^兼容各模块标准的容器
以前我们会将代码包裹在一个简单的匿名函数里,现在需要加一些额外的代码来兼容各种模块标准。
状态容器
用两个对象来存储信息
Object.create(null)
创建的对象不会带有Object.prototype
上的方法,因为我们不需要它们,例如toString
、valueOf
、hasOwnProperty
之类的。构造函数
if (!(this instanceof iceSkating)) return new iceSkating(option);
很多库和框架都有这句,简单说就是不用new生成也可以生成实例。触摸事件
对于触摸事件,在移动端,我们会用
touchEvent
,在pc端,我们则用mouseEvent
。所以我们需要检测支持什么事件。支持touch则认为是移动端,否则为pc端
声明事件函数
pc端和移动端这3个函数是通用的。
初始化事件
touchStart
和transitionDurationEndFn
函数每个实例的容器都会绑定,但是所有实例共用touchMove
和touchEnd
函数,它们只绑定在document
,并且只会绑定一次。使用事件委托有两个好处:touchMove
和touchEnd
也绑定在容器元素上,当鼠标移出容器元素时,我们会“失去控制”。在document
上意味着可以“掌控全局”。过程分析
不会把封装的函数的代码都一一列出来,但会说明它的作用。
触碰瞬间
touchStart函数:
会在触碰的第一时间调用,基本都在初始化state的信息
移动
在移动滑块时,可能滑块正在动画中,这是需要考虑一种特殊情况。滑块的移动应该依据现在的位置计算。 如何知道动画运行中的信息呢,可以使用
window.getComputedStyle(element, [pseudoElt])
,它返回的样式是一个实时的CSSStyleDeclaration
对象。用它取transform
的值会返回一个 2D 变换矩阵,像这样matrix(1, 0, 0, 1, -414.001, 0)
,最后两位就是x,y值。简单封装一下,就可以取得当前动画translate的x,y值了。
touchMove函数:
移动时会持续调用,如果只是点击操作,不会触发touchMove。
translate函数:
如果支持translate3d,会优先使用它,translate3d会提供硬件加速。有兴趣可以看看这篇blog两张图解释CSS动画的性能
触摸结束
touchEnd函数:
在触摸结束时调用。
transitionDurationEndFn函数:
动画执行完成后调用
至此,一个完整的滑动过程结束。
实现轮播
第一时间想到的是使用
setInterval
或者递归setTimeout
实现轮播,但这样做并不优雅。事件循环(EventLoop)中
setTimeout
或setInterval
会放入macrotask
队列中,里面的函数会放入microtask
,当这个macrotask
执行结束后所有可用的microtask
将会在同一个事件循环中执行。我们极端的假设
setInterval
设定为200ms,动画时间设为1000ms。每隔200ms,macrotask
队列中就会插入setInterval
,但我们的动画此时没有完成,所以用setInterval
或者递归setTimeout
的轮播在这种情况下是有问题的。最佳思路是在每次动画结束后再将轮播开启。
轮播函数也相当简单
小结
本文记录了我思考的过程,代码应该还有很多地方值得完善。