-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
滑动效果的原理及实践一个滑动小插件 #3
Labels
Comments
赞一个 |
赞 |
赞一个 |
有待改进的几点:
代码整体不错,多多交流啦 :) |
@julyL 看得非常仔细,谢谢指正。对于第三点,false确实不妥,true应该说能禁止大部分情况(冒泡阶段的事件和部分捕获阶段的事件),除了在iceSkating()之前给容器注册的捕获事件。 |
赞一个 |
你如果transition时间设置成0,岂不是动画瞬间执行完毕?会闪烁不顺畅的吧? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
作者:杨敬卓
转载请注明出处
目录
前言
移动端,滑动是很常见的需求。很多同学都用过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
的轮播在这种情况下是有问题的。最佳思路是在每次动画结束后再将轮播开启。
轮播函数也相当简单
小结
本文记录了我思考的过程,代码应该还有很多地方值得完善。
The text was updated successfully, but these errors were encountered: