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:nonesetTimeout(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 = nullbutton.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 = nullbutton.innerHTML = '点击'}else{// setTimeout(function(){//     element.style.opacity = '1'//     button.innerHTML = '重置'// },10)window.getComputedStyle(element, null).opacityelement.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 = nullbutton.innerHTML = '点击'}else{// setTimeout(function(){//     element.style.opacity = '1'//     button.innerHTML = '重置'// },10)// window.getComputedStyle(element, null).opacityelement.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 firedsetTimeout(function(){if (fired) returnwrappedCallback.call(that)}, ((duration + delay) * 1000) + 25)}

参考链接

zepto动画模块源码
transitionend事件MDN
transtion属性详解MDN
transitionend事件详解
Window.getComputedStyle() 方法

转载于:https://www.cnblogs.com/GeniusLyzh/p/8324811.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252474.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

封装一个ViewPager真正的实现图片无限循环滚动带导航点

效果图&#xff1a; 大家在写项目的过程中常常会碰到须要实现Viewpager里面载入几张图片来循环自己主动轮播的效果&#xff0c;假设不封装一下的话代码分散在activity里面会显得非常乱。并且也不利于我们下次复用&#xff0c;所以这里我把viewpager的相关代码抽取出来放在了一个…

毕业论文页眉页脚页码插入

用word这么多年&#xff0c;第一次完整的操作了一遍页眉页脚页码的插入过程&#xff0c;其实三者都要要求奇偶页不同 1.页面布局-》右下角箭头-》版式-》奇偶页不同 因为文章不同的部分需要插入不同的页眉页脚页码&#xff0c;所以要在不同的部分插入分解符断开它们的连接 2、…

巴黎市中心降下2019年第一场雪

当地时间1月22日&#xff0c;法国巴黎市中心降下2019年第一场雪&#xff0c;气温也随之下降&#xff0c;街上的行人和车辆均有所减少。中新社记者 李洋 摄一对情侣在埃菲尔铁塔前合影留念。无家可归者在长椅上睡觉。游客在卢浮宫前拍照。

Echarts实现隐藏x轴,y轴,刻度线,网格

"yAxis": [{//就是一月份这个显示为一个线段&#xff0c;而不是数轴那种一个点点"show" : true,"boundaryGap": true,"type": "category","name": "时间","data": ["1月", "2…

Ubuntu16.04 + Matlab2018+ desktop creation

https://blog.csdn.net/m0_37601622/article/details/82731879 https://blog.csdn.net/l18092482025/article/details/78906436 The second blog teaches you how to download a matlab.png when there is no matlab.png in /usr/share/applications/.

Atom插件主题推荐

注意事项 主题和插件这方面,比 Sublime Text 人性化多了..一些比较用心的作者增加了二度设置功能。 何为二度设置,就是不用手写代码修改配置文件&#xff0c;点点鼠标&#xff0c;填填输入框就能生效&#xff0c;主题以 isotope-ui 这个做例子介绍,看图&#xff1a; 进入二度设…

印尼发生洪灾和山体滑坡 致多人死亡数千人撤离

当地时间1月23日&#xff0c;印尼南苏拉威西省望加锡居民受洪水影像&#xff0c;用竹筏运送摩托车。近日&#xff0c;印尼南苏拉威西省暴雨连连&#xff0c;造成洪灾和山体滑坡。目前&#xff0c;暴雨引发的洪灾和山体滑坡至少已造成8人死亡&#xff0c;数千人被迫撤离家园。。…

Django学习笔记第三篇--关于响应返回

一、返回简单类型&#xff1a; 1 #1、返回简单字符串 2 #from django.http import HttpResponse 3 return HttpResponse("return string") 4 #2、返回json 5 return HttpResponse(json.dumps(response_data),content_type"application/json") 二、返回文…

HTTP响应状态码

1XX:代表提示信息 2XX:代表成功信息 3XX:代表重定向 4XX:代表客户端错误信息 5XX:代表服务器错误 信息 500:500 错误是服务器内部错误 ,而且是程序上错误 为多,可能是你的用户权限的问题导致,或者是数据库连接出现了错误. 501:服务器501服务器501错误是服务器是否具有请求功能.…

pycharm中无法import已经安装的ros中的库

使用pycharm写python程序&#xff0c;无法import 已经安装的ros包&#xff0c;并提示ImportError: No module named sensor_msgs.msg 解决方法如下 1. pycharm->file->settings->project:csvfile->project interpreter-> 点击右侧下三角选择 show all 2. 在弹…

tcp/ip知识点的总结

知识点搜集于网络&#xff0c;在加上自己的总结&#xff0c;还有很多不全&#xff0c;希望大家多提意见&#xff0c;共享学习&#xff01; 一、TCP/IP是什么&#xff1f; TCP/IP是一个协议族&#xff0c;而不是单独的协议。包括arp、ip、icmp、tcp、udp、http、ftp等协议 二、T…

搜索引擎ElasticSearchV5.4.2系列一之ES介绍

相关博文&#xff1a; 搜索引擎ElasticSearchV5.4.2系列一之ES介绍 搜索引擎ElasticSearchV5.4.2系列二之ElasticSearchV5.4.2kibanaV5.4.2x-packV5.4.2安装 搜索引擎ElasticSearchV5.4.2系列三之ES使用 Elasticsearch 是一个开源的搜索引擎&#xff0c;建立在一个全文搜索引擎…

[译] 前端组件设计原则

原文地址&#xff1a;Front end component design principles 原文作者&#xff1a;Andrew Dinihan 文中示例代码&#xff1a;传送门 限于个人能力&#xff0c;如有错漏之处&#xff0c;烦请不吝赐教。 前言 我在最近的工作中开始使用 Vue 进行开发&#xff0c;但是我在上一家公…

imu_utils标定imu问题解决

在编译过程中遇到的一些问题可以参照这个女生的文章 https://blog.csdn.net/fang794735225/article/details/92804030 下面是imu_utils的主页&#xff0c;可以下载代码&#xff0c;也有使用步骤 https://github.com/gaowenliang/imu_utils 我下载了imu_utils主页最后面提供…

术语-服务:PaaS

ylbtech-术语-服务&#xff1a;PaaSPaaS是Platform-as-a-Service的缩写&#xff0c;意思是平台即服务。 把服务器平台作为一种服务提供的商业模式。通过网络进行程序提供的服务称之为SaaS(Software as a Service)&#xff0c;而云计算时代相应的服务器平台或者开发环境作为服务…

PostgreSQL安装和简单配置

PostgreSQL安装与使用 目录 依赖包的安装源码编译和安装初始化数据库集簇简单配置依赖包安装 PostgreSQL源码安装依赖以下四个软件包 readline zlib flex bison 在Ubuntu中可是应用以下命令直接进行安装&#xff1a; sudo apt-get install libreadline6 libreadline6-dev zlib1…

1.App瘦身经验总结

为什么apk越来越大&#xff1f; 1.项目不断发展&#xff0c;功能越多&#xff0c;代码量增加的同时&#xff0c;资源文件也在不断的增多2.app支持的主流dpi越来越多&#xff0c;如ldpi、mdpi、hdpi、xh xxh xxxh等等&#xff0c;间接导致资源增多3.引入的第三方sdk或开源库越来…

研究相机和IMU坐标系变换

刚开始录制的数据时没有考虑相机和IMU之间的坐标变换&#xff0c;但是后来发现跟踪效果不好&#xff0c;去查验imu数据时&#xff0c;发现&#xff0c;我采集保存的imu数据格式没有和euroc数据集中的imu数据保存格式统一&#xff0c;所以需要研究的是在euroc用于数据采集的设备…

第五天:Swift拖动 item 重排 CollectionView

参考链接:https://www.jianshu.com/p/96f956f1479e 1 import UIKit2 3 enum VC: String {4 case ViewController5 case CollectionViewController6 7 func segueIdentifier() -> String {8 switch self {9 case .ViewController:10 …

MIT Kimera阅读笔记

这两天在调研SLAM的最新算法&#xff0c;找到了2019CVPR上的一篇文章&#xff0c;出自于MIT&#xff0c;因为要给其他同事讲解&#xff0c;所以就把文章的重点内容在我个人理解的情况下翻译了出来&#xff0c;有理解不到位的还请各位大佬多多批评指正。 最后附上了Delaunay Tri…