当前位置: 首页 > Web前端 > HTML

滑动效果原理与实践一款滑动小插件

时间:2023-04-02 12:45:34 HTML

本文转载自博客,转载请注明出处目录前言基本原理HTML结构实践总结前言移动端,滑动是很常见的需求。很多同学都用过swiper.js。本文从原理出发,实现了一个类似swiper的滑动插件滑冰。小插件示例:移动端PC端写代码过程中的一些思考:滑动的原理是什么,动画完成事件如何判断绑定到哪个元素,是否可以使用事件委托来优化PC端和移动端之间的滑动。如何在触摸正在进行的动画时获取当前样式如何实现轮播的基本原理滑动是使用transform:translate(x,y)或者transform:translate3d(x,y,z)来控制元素的移动,以及松手时确定元素在最终位置,元素的样式应用transform:translate3d(endx,endy,0)和transition-duration:time来实现动画恢复效果。标准浏览器提供了transitionend事件来监听动画的结束,并在结束时将动画时间重置为零。注意:这里不讨论非标准浏览器的实现。对于不支持transform和transition的浏览器,可以使用position:absolute配合left和top进行移动,然后使用基于时间的动画算法来模拟动画效果。html结构示例基本结构://exampleSlide1

幻灯片2
幻灯片3
转换:translate3d(x,y,z)应用于className为ice-slide的元素。css代码这里就不展示了,完整的css可以在ice-skating的示例文件中查看。css代码不唯一,简单的说,只要实现下图所示的结构即可。从图中可以直观的看出绿色元素在移动。className为ice-slide的元素的宽度乘以当前索引(offsetWidth*index),即每次稳定时的偏移量。比如一开始的transform:translate3d(offsetWidth*0,0,0),切换到slide2之后,transform:translate3d(offsetWidth*1,0,0),大致是这样的过程。练习源码位于滑冰的dist/iceSkating.js中。我把这个插件命名为ice-skating,希望它能像在冰上一样流畅^_^兼容各个模块的标准容器以前我们会把代码包裹在一个简单的匿名函数中,现在我们需要添加一些额外的代码以与每个模块A模块标准兼容。(function(global,factory){typeofexports==='object'&&typeofmodule!=='undefined'?factory(exports):typeofdefine==='function'&&define.amd?define(['exports'],factory):(factory((global)));}(this,(function(exports){'usestrict';})));state容器使用两个对象来存储信息一个页面可以实例化很多个slides对象,mainStore存储每个对象的信息,比如宽高,配置参数等等。State存储触摸等临时信息,每次触摸后都会被清除。varmainStore=Object.create(null);varstate=Object.create(null);Object.create(null)创建的对象不会有Object.prototype上的方法,因为我们不需要它们,比如toString、valueOf、hasOwnProperty等。构造函数iceSkating(option){if(!(thisinstanceoficeSkating))returnnewiceSkating(option);}iceSkating.prototype={}if(!(thisinstanceoficeSkating))returnnewiceSkating(option);许多库和所有框架都有这句话。简单的说就是不用newgeneration就可以生成instance。触摸事件对于触摸事件,在移动端,我们会使用touchEvent,在pc端,我们会使用mouseEvent。所以我们需要检测支持哪些事件。iceSkating.prototype={support:{touch:(function(){return!!(('ontouchstart'inwindow)||window.DocumentTouch&&documentinstanceofDocumentTouch);})()}如果支持touch,则考虑作为移动端,否则PC端varevents=ic.support.touch?['touchstart','touchmove','touchend']:['mousedown','mousemove','mouseup'];PC端和移动端声明事件函数3个函数是通用的。vartouchStart=function(e){};vartouchMove=function(e){};vartouchEnd=function(e){};initeventvaric=this;varinitEvent=function(){varevents=ic.support。触碰?['touchstart','touchmove','touchend']:['mousedown','mousemove','mouseup'];vartransitionEndEvents=['webkitTransitionEnd','transitionend','oTransitionEnd','MSTransitionEnd','msTransitionEnd'];对于(vari=0;i0||state.diffY>0){//切换到上一个滑块moveTo(currStore,currStore.index-1);}else{//切换到下一个滑块moveTo(currStore,currStore.index+1);}}};反式itionDurationEndFn函数:动画执行后调用vartransitionDurationEndFn=function(){//设置动画状态为falseic.store.animating=false;//执行自定义iceEndCallBack函数if(typeofic.store.iceEndCallBack==='function')ic.store.iceEndCallBack();//将动画时间设置为零transitionDuration(container,0);//清除状态if(ic.store.id===state.id)state=Object.create(null);};至此,一个完整的滑动流程就结束了,实现了轮播。首先想到的是使用setInterval或者递归setTimeout来实现轮播,但是这样并不优雅。在事件循环(EventLoop)中,setTimeout或setInterval会被放到macrotask队列中,里面的函数会被放到microtask中。当宏任务执行结束时,所有可用的微任务将在同一个事件循环中执行。我们极端的假设是setInterval设置为200ms,动画时间设置为1000ms。每隔200ms,setInterval就会插入到macrotask队列中,但是此时我们的动画还没有完成,所以这种情况下设置Interval或者递归setTimeout的轮播是有问题的。最好的办法是在每个动画结束后打开轮播。//动画结束时执行的函数:vartransitionDurationEndFn=function(){...//检查轮播是否开启if(ic.store.autoPlay)autoPlay(ic.store);};轮播功能也很简单varautoPlay=function(store){store.autoPlayID=setTimeout(function(){//当前滑块的索引varindex=store.index;++index;//最后一个,重置为0if(index===store.childLength){index=0;}//movemoveTo(store,index);},store.autoplayDelay);};总结这篇文章记录了我的思考过程,代码中应该有很多值得改进的地方。