原生js判断css3动画过度(transition)结束 transitionend事件

上图的 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

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);

  

我们进行封装一下;封装后的直接调用如下;参考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延伸出来