原生js判断css3动画过度(transition)结束 transitionend事件 以及关键帧keyframes动画结束(animation)回调函数 animationEnd 以及 css 过渡 transition无效
上图的 demo 主要讲的 是 css transition的过渡回调函数transitionend事件;
css3 的时代,css3--动画 一切皆有可能;
传统的js 可以通过回调函数判断动画是否结束;即使是采用CSS技术生成动画效果,JavaScript仍然能捕获动画或变换的结束事件;
transitionend
事件和animationend
事件标准的浏览器事件,但在WebKit浏览器里你仍然需要使用webkit
前缀,所以,我们不得不根据各种浏览器分别检测事件
1 2 3 4 5 6 | var transitions = { 'transition' : 'transitionend' , 'OTransition' : 'oTransitionEnd' , 'MozTransition' : 'transitionend' , 'WebkitTransition' : 'webkitTransitionEnd' } |
下面附上源代码:
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <!doctype html> <html> <head> <meta charset= "utf-8" > <title>suface js判断css动画是否结束</title> </head> <body> <p>一旦动画或变换结束,回调函数就会触发。不再需要大型类库支持。<br> </p> <style type= "text/css" > .sample { width: 200px; height: 200px; border: 1px solid green; background: lightgreen; opacity: 1; margin-bottom: 20px; transition-property: opacity; /*transition-duration: .5s;*/ transition-duration:3s; } .sample.hide { opacity: 0; } </style> <div class = "sample" >css3动画过度慢慢隐藏(transition-duration:3s;)</div> <p><button onclick= "this.style.display='none';startFade();" >慢慢消退,检测结束事件</button></p> <script> ;( function () { var e = document.getElementsByClassName( 'sample' )[0]; function whichTransitionEvent(){ var t, el = document.createElement( 'surface' ), transitions = { 'transition' : 'transitionend' , 'OTransition' : 'oTransitionEnd' , 'MozTransition' : 'transitionend' , 'WebkitTransition' : 'webkitTransitionEnd' } for (t in transitions){ if ( el.style[t] !== undefined ){ return transitions[t]; } } } var transitionEvent = whichTransitionEvent(); transitionEvent && e.addEventListener(transitionEvent, function () { alert( 'css3运动结束!我是回调函数,没有使用第三方类库!' ); e. removeEventListener(transitionEvent,arguments.callee, false ); //销毁事件 }); startFade = function () { e.className+= ' hide' ; } })(); </script> </body> </html><br><br> //兼容性 详情 |
另外,注意一下:在js调用中;transitionend 存几个个问题:
如果 transition 中:变换的属性有多个 ;比如设置 宽高 过度(transition :width:.2s,height:.2s), transitionend 事件会促发2次(宽1次,高1次,每个属性都会触发 一次)
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 | <style> .surfaces_box{ background:url(../../loveImg/QioA-fxehfqi8208393.jpg) no-repeat center center;;width:550px;;height:343px;; margin:0 auto; position: relative;} .surfaces_box p{ position:absolute; bottom:0; left:0; right:0; color: #333; text-align:center; padding:10px 0; background: rgba(255,255,255,.4) } .surfaces{width:100px;height:100px;background:red; color: #fff; text-align:center; transition:width 1s ,height 1s;} </style> <div class = "surfaces_box" > <div class = "surfaces" id= "j_surfaces" >click me </div> <p>http: //www.cnblogs.com/surfaces/</p> </div> <script> function addEnd(obj,fn){ obj.addEventListener( 'WebkitTransitionEnd' ,fn, false ); obj.addEventListener( 'transitionend' ,fn, false ); } var surfaces=document.getElementById( "j_surfaces" ); surfaces.onclick= function (){ this .style.width= this .offsetWidth+100+ "px" ; this .style.height= this .offsetHeight+100+ "px" ; }; addEnd(surfaces, function (){ alert( 'CSS3 过渡结束回调 ' ); }); </script> |
如果 transition 中:变换的属性 (transition :width:.1s);transitionend 之后再次改变 宽度; 再次促发 transition类似递归;
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 | <style> .surfaces_box{ background:url(../../loveImg/QioA-fxehfqi8208393.jpg) no-repeat center center;;width:550px;;height:343px;; margin:0 auto; position: relative;} .surfaces_box p{ position:absolute; bottom:0; left:0; right:0; color: #333; text-align:center; padding:10px 0; background: rgba(255,255,255,.4) } .surfaces{width:100px;height:100px;background:red; color: #fff; text-align:center; transition:width 1s} </style> <div class = "surfaces_box" > <div class = "surfaces" id= "j_surfaces" >click me </div> <p>http: //www.cnblogs.com/surfaces/</p> </div> <script> function addEnd(obj,fn){ obj.addEventListener( 'WebkitTransitionEnd' ,fn, false ); obj.addEventListener( 'transitionend' ,fn, false ); } var surfaces=document.getElementById( "j_surfaces" ); surfaces.onclick= function (){ this .style.width= this .offsetWidth+100+ "px" ; }; addEnd(surfaces, function (){ this .style.width= this .offsetWidth+100+ "px" ; alert( 'CSS3 过渡结束回调 ' ); }); </script> |
如果元素原先display:none 到block,transition 过渡无效;可能导致transitionend 失效;举个例子 dom元素从display:none 到block ,dom的opacity从0到1的 transition没过渡 ;
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 | <style> .surfaces_box{ background:url(../../loveImg/QioA-fxehfqi8208393.jpg) no-repeat center center;;width:550px;;height:343px;; margin:0 auto; position: relative;} .surfaces_box p{ position:absolute; bottom:0; left:0; right:0; color: #333; text-align:center; padding:10px 0; background: rgba(255,255,255,.4) } .surfaces{width:100px;height:100px;background:red; color: #fff; text-align:center; transition:width 1s} .surfaces_box .hideElement{width:100px;height:100px; position:absolute; right:0; top:0; background:red; color: #fff; text-align:center; transition:opacity 1s; display:none; opacity:0;} </style> <div class = "surfaces_box" > <div class = "surfaces" id= "j_surfaces" >click me </div> <div class = "hideElement" id= "j_hideElement" >opacity transition</div> <p>http: //www.cnblogs.com/surfaces/</p> </div> <script> var j_hideElement=document.getElementById( "j_hideElement" ); var surfaces=document.getElementById( "j_surfaces" ); surfaces.onclick= function (){ j_hideElement.style.display= 'block' ; //// 原先display=none j_hideElement.style.opacity=1; // transition:opacity 1s; }; </script> |
上图 dom元素 从none到block,导致 transition-duration 无法渲染;
1 一般是这样解决 加个计时器延迟
2 或者 强制 /强制 获取当前的内联样式
3或者重绘
都是从none到block ,dom元素刚生成未能即使渲染,导致过度失效,所以主动触发页面回流(重绘),刷新DOM;
更改 offsetTop、offsetLeft、 offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientTop、clientLeft、clientWidth、clientHeight;getComputedStyle() 、currentStyle()。这些都会触发回流。回流导致DOM重新渲染,平时要尽可能避免,但这里,为了动画即时生效播放,则主动触发回流,刷新DOM。
4 另外 部分低端安卓机型或者wp手机 无法促发 transitionend事件 需要主动触发一次
以上综合解决方式大致如下;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var fired = false ; var handler = function () { //回调函数中解绑事件 callback && callback.apply(obj,arguments); fired= true ; obj.removeEventListener(transitionEnd,arguments.callee, false ) }; if (obj.addEventListener){ obj.addEventListener(transitionEnd, handler, false ); } setTimeout( function (){ //绑定过事件还做延时处理,是transitionEnd在older Android phones不一定触发 if (fired) return handler() },(duration + delay) + 25); |
1 | 本文地址:<a href= "http://www.cnblogs.com/surfaces/" target= "_blank" >http://www.cnblogs.com/surfaces/</a> |
我们进行封装一下;封装后的直接调用如下;参考zepto 改写;不依赖任何类库,详情源码调用例子 点击transform.js 查看;该函数兼容性与zepto一致;
transform(dom元素,{ css属性:css值},transitionDuration(单位:毫秒),transitionTiming,transitionend回调函数,transitionDelay(单位:毫秒));
transform(dom元素,keyframesName,animationDuration,animationTiming,animationend回调函数,animationDelay(单位:毫秒));
参数说明:
/* * js transfrom.js * @param obj {obj} 原生dom对象 * @param properties {json} ||string { translate3d:'220px,10px,0',left:'1em',opacity:0.2, rotateY:'30deg'} || animationName 多个可以以逗号分割 如 'fadeIn,sliderDown'; * @param duration {number} 默认400毫秒 可省略 * @param ease {str} 默认linear,可省略 支持 cubic-bezier(0.42,0,1,1)写法; * @param callback {function} 回调函数 可省略 * @param delay {number} 延迟时间 可省略 */
多种参数 调用写法示例:
/* http://www.cnblogs.com/surfaces * @param properties 为 {} 或者 string ;如果 properties= string 为animation- name * transform(elem, properties) * transform(elem, properties, ease) * transform(elem, properties, ease, delay) * transform(elem, properties, ease, callback, delay) * transform(elem, properties, callback) * transform(elem, properties, callback, delay) * transform(elem, properties, duration ) * transform(elem, properties, duration, ease) * transform(elem, properties, duration, delay) * transform(elem, properties, duration, callback) * transform(elem, properties, duration, callback,delay) * transform(elem, properties, duration, ease, delay) * transform(elem, properties, duration, ease, callback) * transform(elem, properties, duration, ease, callback,delay) //使用示例如下: transform(elem,{translateX:'150px',left:'1em',opacity:0.2,perspective:'400px', rotateY:'40deg'},600,'linear', function(){ console.log('transition结束回调') },200) ; transform(elem, keyframesName,600,'linear',function(){ console.log('animation结束回调') },200) ; */
不要搞混css3动画事件 webkitAnimationEnd 事件
至于animation关键帧动画结束,提供了3个api;如下
开始事件 webkitAnimationStart
结束事件 webkitAnimationEnd
重复运动事件 webkitAnimationIteration;
1 | 本文地址:<a href= "http://www.cnblogs.com/surfaces/" target= "_blank" >http://www.cnblogs.com/surfaces/</a> |
总结:
相同点:
两者都在移动端大放光彩;利用GPU加速性能,相对流畅;
利用 js进行对动画结束事件可以捕捉监听;
区别之处
transition 只有唯一的事件 transitionend,而animation 有3个;
transition 强调过渡,Transition + Transform = 两个关键帧的Animation
animation 强调流程与控制,Duration + TransformLib + Control = 多个关键帧的Animation
animation 可以实时捕捉操作修改属性, 而transition无法捕捉 中间过程;
transition 往往需要事件驱动,hover,click之类 促发,animation从flash延伸出来
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!