场景描述

在使用JS控制动画时一般需要在动画结束后执行回调去进行DOM的相关操作,所以需要监听动画结束进行回调。JS提供了以下事件用于监听动画的结束,简单总结学习下。

CSS3动画监听事件

transitionEnd事件

transitionEnd事件会在CSS transition动画结束后触发。

动画结束后触发监听事件

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
.demo{
margin:100px;
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
}
.demo:hover{
width: 200px;
}
</style>
</head>
<body>
<div id="demo" class="demo">
鼠标移入
</div>
<script type="text/javascript">
var element = document.getElementById('demo')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
</script>
</body>
</html>

事件多次触发问题

当存在多个属性过渡变化时,结束时会多次触发transitionend事件。看个例子:

当过渡结束时,width和background-color都发生变化,会触发两次transionend事件

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
}
.w200{
width: 200px;
background-color: #fef;
}
</style>
</head>
<body>
<div id="demo" class="demo" onmouseover="change()" onmouseout="change()">
</div>
<script type="text/javascript">
var element = document.getElementById('demo')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo w200': 'demo'
}
</script>
</body>
</html>

事件失效问题及解决方案

1、在transiton动画完成前设置display:none,事件不会触发。

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
}
.w200{
width: 200px;
}
</style>
</head>
<body>
<div id="demo" class="demo" onmouseover="change()" onmouseout="change()">
</div>
<script type="text/javascript">
var element = document.getElementById('demo')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo w200': 'demo'
// 500ms后设置display:none
setTimeout(function (){
element.style.display = 'none'
},400)
}
</script>
</body>
</html>

2、当transition完成前移除transition一些属性时,事件也不会触发,例如:

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
}
.noTranstion{
width:100px;
height: 100px;
background-color: #ddc;
}
.w200{
width: 200px;
}
</style>
</head>
<body>
<div id="demo" class="demo" onmouseover="change()" onmouseout="change()">
</div>
<script type="text/javascript">
var element = document.getElementById('demo')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo w200': 'demo'
setTimeout(function(){
element.className = 'noTranstion'
},400)
}
</script>
</body>
</html>

3、元素从display:none到block,不会有过渡,导致无法触发transitionend事件

例如:元素从display:none 到block opacity从0到1,无法触发过渡效果。

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
body{padding: 50px;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
opacity:0;
display: none;
}
.noTranstion{
width:100px;
height: 100px;
background-color: #ddc;
}
.opt{
display: block;
opacity:1
} .w200{
width: 200px;
}
button{position: absolute;top: 200px;width: 100px;height: 40px;}
</style>
</head>
<body>
<div id="demo" class="demo" onmouseover="change()" onmouseout="change()">
</div>
<button onclick="change()">Click</button>
<script type="text/javascript">
var element = document.getElementById('demo')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo opt': 'demo'
}
</script>
</body>
</html>

无法触发过渡效果原因:

元素从none到block,刚生成未能即时渲染,导致过渡失效。所以需要主动触发页面重绘,刷新DOM。页面重绘可以通过改变一些CSS属性来触发,例如:offsetTop、offsetLeft、offsetWidth、scrollTop等。

触发过渡效果解决方案:

1、通过定时器延迟渲染

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
body{padding: 50px;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
opacity: 0;
display: none;
}
.opt{
display: block;
}
button{position: absolute;top: 200px;width: 100px;height: 40px;}
</style>
</head>
<body>
<div id="demo" class="demo">
</div>
<button id="button" onclick="change()">点击</button>
<script type="text/javascript">
var element = document.getElementById('demo')
var button = document.getElementById('button')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo opt': 'demo'
if(element.className === 'demo'){
element.style.opacity = null
button.innerHTML = '点击'
}else{
setTimeout(function(){
element.style.opacity = '1'
button.innerHTML = '重置'
},10)
}
}
</script>
</body>
</html>

2、强制获取当前内联样式

通过window.getComputedStyle()方法返回应用样式后的元的所有CSS属性的值,并解析这些值可能包含的任何基本计算。也就是说返回的属性值是已计算后的值,即DOM元素的样式已经更新了。然后再改变对应属性值触发过渡效果。例如:

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
body{padding: 50px;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
opacity: 0;
display: none;
}
.opt{
display: block;
}
button{position: absolute;top: 200px;width: 100px;height: 40px;}
</style>
</head>
<body>
<div id="demo" class="demo">
</div>
<button id="button" onclick="change()">点击</button>
<script type="text/javascript">
var element = document.getElementById('demo')
var button = document.getElementById('button')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo opt': 'demo'
if(element.className === 'demo'){
element.style.opacity = null
button.innerHTML = '点击'
}else{
// setTimeout(function(){
// element.style.opacity = '1'
// button.innerHTML = '重置'
// },10)
window.getComputedStyle(element, null).opacity
element.style.opacity = '1'
button.innerHTML = '重置'
}
}
</script>
</body>
</html>

3、触发重绘刷新DOM

通过clientWidth触发重绘,例如:

<!DOCTYPE html>
<html>
<head>
<title>transtionend demo</title>
<style type="text/css">
*{margin:0;padding: 0;}
body{padding: 50px;}
.demo{
width:100px;
height: 100px;
background-color: #ddc;
transition: all 0.5s ease-out;
opacity: 0;
display: none;
}
.opt{
display: block;
}
button{position: absolute;top: 200px;width: 100px;height: 40px;}
</style>
</head>
<body>
<div id="demo" class="demo">
</div>
<button id="button" onclick="change()">点击</button>
<script type="text/javascript">
var element = document.getElementById('demo')
var button = document.getElementById('button')
element.addEventListener('transitionend', handle, false)
function handle(){
alert('transitionend事件触发')
}
function change() {
element.className = element.className === 'demo' ? 'demo opt': 'demo'
if(element.className === 'demo'){
element.style.opacity = null
button.innerHTML = '点击'
}else{
// setTimeout(function(){
// element.style.opacity = '1'
// button.innerHTML = '重置'
// },10)
// window.getComputedStyle(element, null).opacity
element.clientWidth;
element.style.opacity = '1'
button.innerHTML = '重置'
}
}
</script>
</body>
</html>

浏览器兼容性

移动端基本支持 android2.1+、webkit3.2+

详见浏览器兼容性

animationEnd事件

与transitonend事件类似,详见

Zepto中animate结束回调实现

查看了下zepto动画模块的源代码,animate()方法在动画结束后触发回调也是通过transitionend、animationend事件来触发。

另外在一些低版本的Android手机可能无法触发transitionend事件,需要手动触发。

$.fx = {
off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
speeds: { _default: 400, fast: 200, slow: 600 },
cssPrefix: prefix,
transitionEnd: normalizeEvent('TransitionEnd'),
animationEnd: normalizeEvent('AnimationEnd')
}
// 手动触发事件
if (duration > 0){
this.bind(endEvent, wrappedCallback)
// transitionEnd is not always firing on older Android phones
// so make sure it gets fired
setTimeout(function(){
if (fired) return
wrappedCallback.call(that)
}, ((duration + delay) * 1000) + 25)
}

参考链接

zepto动画模块源码

transitionend事件MDN

transtion属性详解MDN

transitionend事件详解

Window.getComputedStyle() 方法

JS如何监听动画结束的更多相关文章

  1. js 实时监听input中值变化

    注意:用到了jquery需要引入jquery.min.js. 需求: 1.每个地方需要分别打分,总分为100; 2.第一个打分总分为40; 3.第二个打分总分为60. 注意:需要判断null.&quo ...

  2. js 事件监听 冒泡事件

    js 事件监听  冒泡事件   的取消 [自己写框架时,才有可能用到] <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitiona ...

  3. js 事件监听 兼容浏览器

    js 事件监听 兼容浏览器   ie 用 attachEvent   w3c(firefox/chrome)  用 addEventListener 删除事件监听 ie 用 detachEven   ...

  4. js事件监听机制(事件捕获)总结

    在前端开发过程中我们经常会遇到给页面元素添加事件的问题,添加事件的js方法也很多,有直接加到页面结构上的,有使用一些js事件监听的方法,由于各个浏览器对事件冒泡事件监听的机制不同,le浏览器只有事件冒 ...

  5. js动态监听dom变化

    原生js 动态监听dom变化,根据不同的类型绑定不同的处理逻辑 // Firefox和Chrome早期版本中带有前缀   var MutationObserver = window.MutationO ...

  6. js事件监听机制(事件捕获)

    在前端开发过程中我们经常会遇到给页面元素添加事件的问题,添加事件的js方法也很多,有直接加到页面结构上的,有使用一些js事件监听的方法,由于各个浏览器对事件冒泡事件监听的机制不同,le浏览器只有事件冒 ...

  7. Vue.js:监听属性

    ylbtech-Vue.js:监听属性 1.返回顶部 1. Vue.js 监听属性 本章节,我们将为大家介绍 Vue.js 监听属性 watch,我们可以通过 watch 来响应数据的变化: 实例 & ...

  8. angular 监听ng-repeat结束时间

    有些时候我们想要监听angular js中的 ng-repeat结束事件,从而好初始化一些我们的第三方或者其他需要初始化的js. 我们可以这样处理: js 中这样定义 : //监听事件 是否加载完毕a ...

  9. [转] js对象监听实现

    前言 随着前端交互复杂度的提升,各类框架如angular,react,vue等也层出不穷,这些框架一个比较重要的技术点就是数据绑定.数据的监听有较多的实现方案,本文将粗略的描述一番,并对其中一个兼容性 ...

随机推荐

  1. 【转】Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...

  2. hibernate框架学习之持久化对象OID

    持久化对象唯一标识——OID 1)数据库中使用主键可以区分两个对象是否相同2)Java语言中使用对象的内存地址区分对象是否相同3)Hibernate中使用OID区分对象是否相同Hibernate认为每 ...

  3. codeforces 955F Cowmpany Cowmpensation 树上DP+多项式插值

    给一个树,每个点的权值为正整数,且不能超过自己的父节点,根节点的最高权值不超过D 问一共有多少种分配工资的方式? 题解: A immediate simple observation is that ...

  4. RT-thread嵌入式操作系统相关的问题

    面试中问到 RT-thread嵌入式操作系统相关的问题 RT-thread操作系统调度器的实现细节 RT-Thread中提供的线程调度器是基于优先级的全抢占式调度: 在系统中除了中断处理函数.调度器上 ...

  5. 006_tcpdump专题

    抓包是排查问题非常重要的一种方式,这里汇总常用的排查日常问题的,tcpdump在linux机器上的使用姿势. 一.排查statsd打点情况. 线上出现statsd打点的指标无法正常显示的情况,需要排查 ...

  6. ubuntu安装Qt

    一.安装 卸载旧版本Qt,找到安装目录,例如:/home/likewei/Qt5.11.2,终端输入命令进入该目录,输入命令:./MaintenanceTool      进入图形画面卸载就行了 1. ...

  7. Jenkins pipeline概念理解

      1.Jenkins Pipeline总体介绍 Pipeline,简而言之,就是一台运行于Jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程 ...

  8. ubuntu 精简配置

    是这样的情况,在windows 7的vm虚拟机上装了Ubuntu 12.04 Desktop,主要是想用它的gui, 所以把不要的给删了. sudo apt-get -y --auto-remove ...

  9. ASP.NET MVC5高级编程 之 HTML辅助方法

    Html属性调用HTML辅助方法,Url属性调用URL辅助方法,Ajax属性调用Ajax辅助方法. HTML辅助方法 1.Html.BeginForm @using (Html.BeginForm(& ...

  10. 本地项目提交到github和提交更新(转)

    一:首先当然是去github注册账号了. 二:注册完毕登录后,在自己的首页上面点击右上角“+”号,然后选择New repository,或者直接点击下面的绿色按钮,创建一个新仓库.如图: 然后填入仓库 ...