JS如何监听动画结束
场景描述
在使用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如何监听动画结束的更多相关文章
- js 实时监听input中值变化
注意:用到了jquery需要引入jquery.min.js. 需求: 1.每个地方需要分别打分,总分为100; 2.第一个打分总分为40; 3.第二个打分总分为60. 注意:需要判断null.&quo ...
- js 事件监听 冒泡事件
js 事件监听 冒泡事件 的取消 [自己写框架时,才有可能用到] <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitiona ...
- js 事件监听 兼容浏览器
js 事件监听 兼容浏览器 ie 用 attachEvent w3c(firefox/chrome) 用 addEventListener 删除事件监听 ie 用 detachEven ...
- js事件监听机制(事件捕获)总结
在前端开发过程中我们经常会遇到给页面元素添加事件的问题,添加事件的js方法也很多,有直接加到页面结构上的,有使用一些js事件监听的方法,由于各个浏览器对事件冒泡事件监听的机制不同,le浏览器只有事件冒 ...
- js动态监听dom变化
原生js 动态监听dom变化,根据不同的类型绑定不同的处理逻辑 // Firefox和Chrome早期版本中带有前缀 var MutationObserver = window.MutationO ...
- js事件监听机制(事件捕获)
在前端开发过程中我们经常会遇到给页面元素添加事件的问题,添加事件的js方法也很多,有直接加到页面结构上的,有使用一些js事件监听的方法,由于各个浏览器对事件冒泡事件监听的机制不同,le浏览器只有事件冒 ...
- Vue.js:监听属性
ylbtech-Vue.js:监听属性 1.返回顶部 1. Vue.js 监听属性 本章节,我们将为大家介绍 Vue.js 监听属性 watch,我们可以通过 watch 来响应数据的变化: 实例 & ...
- angular 监听ng-repeat结束时间
有些时候我们想要监听angular js中的 ng-repeat结束事件,从而好初始化一些我们的第三方或者其他需要初始化的js. 我们可以这样处理: js 中这样定义 : //监听事件 是否加载完毕a ...
- [转] js对象监听实现
前言 随着前端交互复杂度的提升,各类框架如angular,react,vue等也层出不穷,这些框架一个比较重要的技术点就是数据绑定.数据的监听有较多的实现方案,本文将粗略的描述一番,并对其中一个兼容性 ...
随机推荐
- struts2框架学习之第三天
day03 上传下载 1 上传下载组件介绍 l jspSmartUpload(model1的年代): l apache-commons-fileupload,Struts2默认上传组 ...
- Linux系统基础优化及常用命令
Linux基础系统优化 引言没有,只有一张图. Linux的网络功能相当强悍,一时之间我们无法了解所有的网络命令,在配置服务器基础环境时,先了解下网络参数设定命令. ifconfig 查询.设置网卡和 ...
- PID控制器开发笔记之十一:专家PID控制器的实现
前面我们讨论了经典的数字PID控制算法及其常见的改进与补偿算法,基本已经覆盖了无模型和简单模型PID控制经典算法的大部.再接下来的我们将讨论智能PID控制,智能PID控制不同于常规意义下的智能控制,是 ...
- Function types cannot have argument labels 错误解决方案
今天在封装网络工具类的时候 报错了 经过分析发现是在Swift3.0 把闭包的入参的参数名去掉就好了 正确写法 completion: @escaping (Any?, Bool)->() 错误 ...
- bat命令行实现全盘遍历搜索文件
背景:当想要查找一个文件时,记得放在某个盘里.手动去遍历时感觉好心累,找了半天还是没有找着(虽然win有自带的搜索框,但是看着进度条的速度,我便果断的点了取消).基于这个情况,所以写了脚本满足自身查找 ...
- java多线程快速入门(二十一)
CountDownLatch(闭锁)计数器 有一个任务A,它要等待其他4个任务执行完毕之后才执行,此时就可以利用CountDownLatch来实现这种功能 package com.cppdy; imp ...
- SpringMVC文件下载与JSON格式
点击查看上一章 现在JSON这种数据格式是被使用的非常的广泛的,SpringMVC作为目前最受欢迎的框架,它对JSON这种数据格式提供了非常友好的支持,可以说是简单到爆. 在我们SpringMVC中只 ...
- 使用pm2离线部署nodejs项目
1.下载https://npm.taobao.org/mirrors/node/v8.11.1/node-v8.11.1-linux-x64.tar.xz 比如安装到/opt目录 xz -d node ...
- 【ftp】主动模式和被动模式
来自:http://blog.csdn.net/liuhelong12/article/details/50218311 原博主不让转载全文,不过下面这部分是原博主转载别人的,所以我拿过来应该没问题吧 ...
- git 注意事项
1,用户凭证 github的两种url地址 http ssh :由于Git和Github交互操作可能会很频繁,那么一定少了用户授权的操作,为了防止每次操作重复输入用户名和密码,Git提供了两 ...