如何判断一个DOM元素正在动画,一个CSS“阻塞”JS的例子
一般情况下CSS不会直接影响JS的程序逻辑,但是以CSS实现动画的话,这个便不太确定了,这个故事发生在与UED迁移全局样式的过程。
曾经我有一段实现弹出层隐藏动画的代码是这个样子的:
- if (this.needAnimat && typeof this.animateHideAction == 'function' && this.status != 'hide') {
- this.animateHideAction.call(this, this.$el);
- } else
- this.$el.hide();
在所有组件中,如果设置了animatHideAction回调的,便会执行其中的动画逻辑,针对弹出层来说:
① alert
② loading
③ toast
④ 底部弹出层
等组件中动画效果各不相同:
① 动画显示时下沉,隐藏时上浮
② 动画渐隐渐显
③ 组件底部弹出
......
针对通用的动画,一般框架会提供一段CSS类做处理,不满足的情况,各个业务团队便需要自己封装:
- cm-fade-in, .cm-fade-out, .cm-down-in, .cm-down-out, .cm-up-in, .cm-up-out {
- -webkit-animation-duration: 0.3s;
- animation-duration: 0.3s;
- -webkit-animation-fill-mode: both;
- animation-fill-mode: both;
- }
- ......
- @keyframes fadeOut {
- 0% {
- opacity:;
- -webkit-transform: scale(1);
- transform: scale(1);
- }
- 100% {
- opacity:;
- -webkit-transform: scale(1.185);
- transform: scale(1.185);
- }
- }
- ......
这个时候我们要实现一个居中弹出层渐隐的效果事实上只需要这样做:
- el.addClass('cm-fade-out');
- el.one($.fx.animationEnd, function () {
- el.removeClass('cm-fade-out');
- el.hide();
- });
在动画结束后将对应的动画class移除,再执行真实的hide方法,隐藏dom结构。
其实,我记得是去年的时候我是这么处理这个代码的,当时被一个同事骂了不严谨,今年就使用了animationEnd接口:
- el.addClass('cm-fade-out');
- setTimeout(function () {
- el.removeClass('cm-fade-out');
- el.hide();
- }, 340);
这里问题来了,使用animationEnd与setTimeout去除动画class,或者执行业务真实逻辑,到底哪家强,哪个合适?
第一反应都是认为animationEnd比较合理,于是我最近遇到了一个问题:
请求一个数据,loading一直在那里转,永远不消失了!而且执行了hideLoading的操作,与数据延迟毫无关系
于是我开始愉快的定位,当时搞了一会,发现loading的动画没有执行,仔细一定位,发现css中的动画相关的css丢了,于是造成的结果是:
- el.addClass('cm-fade-out');
这个代码变成了单纯的class增加,并没有执行动画,也就是,animationEnd的事件没有触发,于是没有执行hide方法,所以loading框就一直在那里转
问题定位到了,解决方案就非常简单了,将css的动画加上即可;但是也说明了,这段代码中JS代码逻辑依赖了CSS相关,从而导致了CSS阻塞JS的假象
这里如果使用setTimeout的话虽然感觉没有animationEnd严谨,但是一定会保证这逻辑代码执行,从某种程度来说,似乎更好,这里的优化代码是:
- var isTrigger = false;
- el.addClass(scope.animateOutClass);
- el.one($.fx.animationEnd, function () {
- isTrigger = true;
- el.removeClass(scope.animateOutClass);
- el.hide();
- });
- setTimeout(function () {
- if (isTrigger) return;
- el.removeClass(scope.animateOutClass);
- el.off($.fx.animationEnd);
- el.hide();
- }, 350);
如果animationEnd执行了便不理睬setTimeout,否则便走setTimeout逻辑,也不至于影响业务逻辑,但是这个似乎不是最优解决方案。
因为我没有办法,因为这里得有350ms的延迟,在不存在css动画的时候,似乎整个弹出层消失逻辑都变得2B了起来,比较好的方式是,我在执行动画前检测是否具有该css比较靠谱
所以,javascript检测CSS的某一个className是否存在,似乎变成了关键,但是就算就算能找到具有某class,这个class也未必具有动画属性,或者该属性被篡改
况且使用document.styleSheets方式去判断某个样式class是否存在,经过之前的经验,本身就是大坑,还会有跨域什么的场景,坑死人,比如这个代码:
- function getAllSelectors() {
- var ret = [];
- for (var i = 0; i < document.styleSheets.length; i++) {
- var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
- for (var x in rules) {
- if (typeof rules[x].selectorText == 'string') ret.push(rules[x].selectorText);
- }
- }
- return ret;
- }
- function selectorExists(selector) {
- var selectors = getAllSelectors();
- for (var i = 0; i < selectors.length; i++) {
- if (selectors[i] == selector) return true;
- }
- return false;
- }
- //调用方式
- selectorExists('.class');
- selectorExists('#id');
上面的代码,本身比较完善了,但是如果某一个css文件跨域的话就完蛋,所以这个方案不靠谱:
① class检测方案本身不靠谱
② 就算class靠谱,也不能保证class就具有动画相关属性,所以也不靠谱!
最终我想到的方案还是对动画属性做检测,检测点主要在动画属性的检测,比如关键属性:
① animation-name
② transition的检测
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <title></title>
- <script id="others_zepto_10rc1" type="text/javascript" class="library" src="http://sandbox.runjs.cn/js/sandbox/other/zepto.min.js"></script>
- <style>
- .cm-fade-in {
- -webkit-animation-name: fadeIn;
- animation-name: fadeIn;
- }
- .cm-fade-out {
- -webkit-animation-name: fadeOut;
- animation-name: fadeOut;
- }
- @-webkit-keyframes fadeIn {
- 0% {
- opacity: 0;
- -webkit-transform: scale(0.815);
- transform: scale(0.815);
- }
- 100% {
- opacity: 1;
- -webkit-transform: scale(1);
- transform: scale(1);
- }
- }
- @keyframes fadeIn {
- 0% {
- opacity: 0;
- -webkit-transform: scale(0.815);
- transform: scale(0.815);
- }
- 100% {
- opacity: 1;
- -webkit-transform: scale(1);
- transform: scale(1);
- }
- }
- @-webkit-keyframes fadeOut {
- 0% {
- opacity: 1;
- -webkit-transform: scale(1);
- transform: scale(1);
- }
- 100% {
- opacity: 0;
- -webkit-transform: scale(1.185);
- transform: scale(1.185);
- }
- }
- @keyframes fadeOut {
- 0% {
- opacity: 1;
- -webkit-transform: scale(1);
- transform: scale(1);
- }
- 100% {
- opacity: 0;
- -webkit-transform: scale(1.185);
- transform: scale(1.185);
- }
- }
- .cm-down-in {
- -webkit-animation-name: downIn;
- animation-name: downIn;
- }
- .cm-down-out {
- -webkit-animation-name: downOut;
- animation-name: downOut;
- }
- @-webkit-keyframes downIn {
- 0% {
- opacity: 0;
- -webkit-transform: translate3d(0, 100%, 0);
- transform: translate3d(0, 100%, 0);
- }
- 100% {
- opacity: 1;
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0);
- }
- }
- @keyframes downIn {
- 0% {
- opacity: 0;
- -webkit-transform: translate3d(0, 100%, 0);
- transform: translate3d(0, 100%, 0);
- }
- 100% {
- opacity: 1;
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0);
- }
- }
- @-webkit-keyframes downOut {
- 0% {
- opacity: 1;
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0);
- }
- 100% {
- opacity: 0;
- -webkit-transform: translate3d(0, 100%, 0);
- transform: translate3d(0, 100%, 0);
- }
- }
- @keyframes downOut {
- 0% {
- opacity: 1;
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0);
- }
- 100% {
- opacity: 0;
- -webkit-transform: translate3d(0, 100%, 0);
- transform: translate3d(0, 100%, 0);
- }
- }
- .cm-up-in {
- -webkit-animation-name: upIn;
- animation-name: upIn;
- }
- .cm-up-out {
- -webkit-animation-name: upOut;
- animation-name: upOut;
- }
- </style>
- </head>
- <body>
- <script type="text/javascript">
- var hasAnimationProperty = function (className) {
- var animateProprtys = [
- //有什么判断的便新增,暂时只判断animation,不同的动画特性,判断方式不一致
- // $.fx.cssPrefix + 'transition',
- $.fx.cssPrefix + 'animation-name'
- ];
- var el = $('<div></div>');
- $('body').append(el);
- var i, len;
- //赋予其class
- el.attr('class', className);
- for (i = 0, len = animateProprtys.length; i < len; i++) {
- if (el.css(animateProprtys[i]) != 'none') return true;
- }
- s = '';
- return false;
- };
- //false
- console.log(hasAnimationProperty('test'));
- //true
- console.log(hasAnimationProperty('cm-up-out'));
- //true
- console.log(hasAnimationProperty('cm-up-in'));
- </script>
- </body>
- </html>
核心代码:
- var hasAnimationProperty = function (className) {
- var animateProprtys = [
- //有什么判断的便新增,暂时只判断animation,不同的动画特性,判断方式不一致
- // $.fx.cssPrefix + 'transition',
- $.fx.cssPrefix + 'animation-name'
- ];
- var el = $('<div></div>');
- $('body').append(el);
- var i, len;
- //赋予其class
- el.attr('class', className);
- for (i = 0, len = animateProprtys.length; i < len; i++) {
- if (el.css(animateProprtys[i]) != 'none') return true;
- }
- s = '';
- return false;
- };
- //false
- console.log(hasAnimationProperty('test'));
- //true
- console.log(hasAnimationProperty('cm-up-out'));
- //true
- console.log(hasAnimationProperty('cm-up-in'));
如此一来,便能判断该class是否具有样式属性了,但是这个代码还需要扩展,而且这么也有性能损害,其中涉及到dom操作了,但是想想动画造成到gpu负担,好像也没什么问题
如何判断一个DOM元素正在动画,一个CSS“阻塞”JS的例子的更多相关文章
- 一个DOM元素绑定多个事件时,先执行冒泡还是捕获
绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照W3C的标准,先发生捕获事件,后发生冒泡事件.所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事 ...
- 一个DOM元素同时拥有多个类名时的样式产生冲突时 属性取决于css样式表中后读取到的属性
如果一个DOM元素包含多个类名,其中的两个类名的属性产生冲突,并不是根据htnl中类名的顺序来决定DOM元素的属性, 而是根据css样式中的顺序来决定DOM元素的属性,它取决于css样式表中后读取到的 ...
- JS判断指定dom元素是否在屏幕内的方法实例
前言 刷网页的时候,有时会遇到这样一个情景,当某个dom元素滚到可见区域时,或者图片的懒加载效果,它就会展现显示动画,十分有趣.那么这是如何实现的呢? 实现原理 想要实现这个功能,就要知道具体的实现原 ...
- C++ vector 删除一个指定元素 和 find 一个指定元素以及遍历删除、 map遍历删除元素和删除find到的元素
vector: 1.delete element 转载:http://www.cnblogs.com/xudong-bupt/p/3522457.html #include <vector> ...
- 前端每日实战:35# 视频演示如何把 CSS 径向渐变用得出神入化,只用一个 DOM 元素就能画出国宝熊猫
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/odKrpy 可交互视频教程 此视频 ...
- vue中一个dom元素可以绑定多个事件?
其实这个问题有多个解决方法的 这里提出两点 第一种 第二种 现在dom上绑定一个 然后在你的methods中直接调用 如果要传参数 这时候千万别忘记 原创 如需转载注明出处 谢谢
- 原生js获取 一个dom元素距离页面可视区域的位置值 -- getBoundingClientRect
getBoundingClientRect() 这个方法返回一个矩形对象,包含四个属性:left.top.right和bottom.分别表示元素各边与页面上边和左边的距离. var box=docum ...
- 35.在CSS中 只用一个 DOM 元素就能画出国宝熊猫
原文地址:https://segmentfault.com/a/1190000015052653 感想: 真神奇! HTML code: <div class="panda" ...
- CSS中可以通过哪些属性定义,使得一个DOM元素不显示在浏览器可视范围内?
最基本的: 设置display属性为none,或者设置visibility属性为hidden 技巧性: 设置宽高为0,设置透明度为0,设置z-index位置在-1000
随机推荐
- SQLServer 各版本区别
SQLServer 2012 新特性 通过AlwaysOn实现各种高可用级别 通过列存储索引技术实现超快速的查询,其中星型链接查询及相似查询的性能提升幅度可高达100倍,同时支持超快速的全文查询 通过 ...
- JS模块化开发:使用SeaJs高效构建页面
一.扯淡部分 很久很久以前,也就是刚开始接触前端的那会儿,脑袋里压根没有什么架构.重构.性能这些概念,天真地以为前端===好看的页面,甚至把js都划分到除了用来写一些美美的特效别无它用的阴暗角落里,就 ...
- MongoDB Java Driver操作指南
MongoDB为Java提供了非常丰富的API操作,相比关系型数据库,这种NoSQL本身的数据也有点面向对象的意思,所以对于Java来说,Mongo的数据结构更加友好. MongoDB在今年做了一次重 ...
- 使用packer制作vagrant centos box
使用packer制作vagrant box:centos 制作vagrant box,网上有教程,可以自己step by step的操作.不过直接使用虚拟在VirtualBox中制作vagrant b ...
- Lind.DDD.Plugins~插件模式的集成
回到目录 对于Lind.DDD这个敏捷框架来说,插件也是其中的一个亮点,所有被认为是插件(Plugins)的模块都会继承自IPlugins这个标示接口,它在程序启动时会找到所有插件,并通过autofa ...
- PHP Excel 下载数据,并分页下载
直接上代码: 调用下载Excel: $total=$duoduo->count(MOD.' as a',$where); $objExcel= SelfExcelObject(); //导出 i ...
- HTML5之应用缓存---manifest---缓存使用----Web前端manifest缓存
相信来查这一类问题的都是遇到问题或者是初学者吧! 没关系相信你认真看过之后就会知道明白的 这是HTML5新加的特性 HTML5 引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连 ...
- MySQL有趣的查询方式
背景介绍 美国大选开始了,国防部要求我对两个总统候选人的票数进行统计.我首先简单的进行一次无条件查询,了解了一下表格的结构及所有数据长什么样子. select * from foo 查询到的结果令我很 ...
- Atitit webservice发现机制 WS-Discovery标准的规范attilax总结
Atitit webservice发现机制 WS-Discovery标准的规范attilax总结 1.1. WS-Discovery标准1 1.2. 一.WS-Discovery1 1.2.1. ...
- AngularJS_01之基础概述、设计原则及MVC设计模式
1.AngularJS: 开源的JS框架,用来开发单一页面应用,以及数据操作频繁的场景:2.设计原则: ①YAGNI原则:You Aren't Gonna Need It! 不要写不需要的代码! ②K ...