如何判斷一個DOM元素正在動畫,一個CSS「阻塞」JS的例子

通常狀況下CSS不會直接影響JS的程序邏輯,可是以CSS實現動畫的話,這個便不太肯定了,這個故事發生在與UED遷移全局樣式的過程。javascript

曾經我有一段實現彈出層隱藏動畫的代碼是這個樣子的:css

1 if (this.needAnimat && typeof this.animateHideAction == 'function' && this.status != 'hide') {
2   this.animateHideAction.call(this, this.$el);
3 } else
4   this.$el.hide();

在全部組件中,若是設置了animatHideAction回調的,便會執行其中的動畫邏輯,針對彈出層來講:html

① alertjava

② loadingweb

③ toast跨域

④ 底部彈出層app

等組件中動畫效果各不相同:框架

① 動畫顯示時下沉,隱藏時上浮dom

② 動畫漸隱漸顯ide

③ 組件底部彈出

......

針對通用的動畫,通常框架會提供一段CSS類作處理,不知足的狀況,各個業務團隊便須要本身封裝:

 1 cm-fade-in, .cm-fade-out, .cm-down-in, .cm-down-out, .cm-up-in, .cm-up-out {
 2   -webkit-animation-duration: 0.3s;
 3           animation-duration: 0.3s;
 4   -webkit-animation-fill-mode: both;
 5           animation-fill-mode: both;
 6 }
 7 ......
 8 @keyframes fadeOut {
 9   0% {
10     opacity: 1;
11     -webkit-transform: scale(1);
12             transform: scale(1);
13   }
14   100% {
15     opacity: 0;
16     -webkit-transform: scale(1.185);
17             transform: scale(1.185);
18   }
19 }
20 ......

這個時候咱們要實現一個居中彈出層漸隱的效果事實上只須要這樣作:

1 el.addClass('cm-fade-out');
2 
3 el.one($.fx.animationEnd, function () {
4   el.removeClass('cm-fade-out');
5   el.hide();
6 });

在動畫結束後將對應的動畫class移除,再執行真實的hide方法,隱藏dom結構。

其實,我記得是去年的時候我是這麼處理這個代碼的,當時被一個同事罵了不嚴謹,今年就使用了animationEnd接口:

1 el.addClass('cm-fade-out');
2 
3 setTimeout(function () {
4   el.removeClass('cm-fade-out');
5   el.hide();
6 }, 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嚴謹,可是必定會保證這邏輯代碼執行,從某種程度來講,彷佛更好,這裏的優化代碼是:

 1 var isTrigger = false;
 2 
 3 el.addClass(scope.animateOutClass);
 4 
 5 el.one($.fx.animationEnd, function () {
 6   isTrigger = true;
 7   el.removeClass(scope.animateOutClass);
 8   el.hide();
 9 });
10 
11 setTimeout(function () {
12   if (isTrigger) return;
13 
14   el.removeClass(scope.animateOutClass);
15   el.off($.fx.animationEnd);
16   el.hide();
17 }, 350);

若是animationEnd執行了便不理睬setTimeout,不然便走setTimeout邏輯,也不至於影響業務邏輯,可是這個彷佛不是最優解決方案。

由於我沒有辦法,由於這裏得有350ms的延遲,在不存在css動畫的時候,彷佛整個彈出層消失邏輯都變得2B了起來,比較好的方式是,我在執行動畫前檢測是否具備該css比較靠譜

因此,javascript檢測CSS的某一個className是否存在,彷佛變成了關鍵,可是就算就算能找到具備某class,這個class也未必具備動畫屬性,或者該屬性被篡改

何況使用document.styleSheets方式去判斷某個樣式class是否存在,通過以前的經驗,自己就是大坑,還會有跨域什麼的場景,坑死人,好比這個代碼:

 1 function getAllSelectors() {
 2   var ret = [];
 3   for (var i = 0; i < document.styleSheets.length; i++) {
 4     var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
 5     for (var x in rules) {
 6       if (typeof rules[x].selectorText == 'string') ret.push(rules[x].selectorText);
 7     }
 8   }
 9   return ret;
10 }
11 
12 function selectorExists(selector) {
13   var selectors = getAllSelectors();
14   for (var i = 0; i < selectors.length; i++) {
15     if (selectors[i] == selector) return true;
16   }
17   return false;
18 }
19 
20 //調用方式
21 selectorExists('.class');
22 selectorExists('#id');

上面的代碼,自己比較完善了,可是若是某一個css文件跨域的話就完蛋,因此這個方案不靠譜:

① class檢測方案自己不靠譜

② 就算class靠譜,也不能保證class就具備動畫相關屬性,因此也不靠譜!

最終我想到的方案仍是對動畫屬性作檢測,檢測點主要在動畫屬性的檢測,好比關鍵屬性:

① animation-name

② transition的檢測

  1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2 <html xmlns="http://www.w3.org/1999/xhtml">
  3 <head>
  4   <title></title>
  5   <script id="others_zepto_10rc1" type="text/javascript" class="library" src="http://sandbox.runjs.cn/js/sandbox/other/zepto.min.js"></script>
  6   <style>
  7    
  8 .cm-fade-in {
  9   -webkit-animation-name: fadeIn;
 10           animation-name: fadeIn;
 11 }
 12 
 13 .cm-fade-out {
 14   -webkit-animation-name: fadeOut;
 15           animation-name: fadeOut;
 16 }
 17 
 18 @-webkit-keyframes fadeIn {
 19   0% {
 20     opacity: 0;
 21     -webkit-transform: scale(0.815);
 22             transform: scale(0.815);
 23   }
 24   100% {
 25     opacity: 1;
 26     -webkit-transform: scale(1);
 27             transform: scale(1);
 28   }
 29 }
 30 
 31 @keyframes fadeIn {
 32   0% {
 33     opacity: 0;
 34     -webkit-transform: scale(0.815);
 35             transform: scale(0.815);
 36   }
 37   100% {
 38     opacity: 1;
 39     -webkit-transform: scale(1);
 40             transform: scale(1);
 41   }
 42 }
 43 @-webkit-keyframes fadeOut {
 44   0% {
 45     opacity: 1;
 46     -webkit-transform: scale(1);
 47             transform: scale(1);
 48   }
 49   100% {
 50     opacity: 0;
 51     -webkit-transform: scale(1.185);
 52             transform: scale(1.185);
 53   }
 54 }
 55 @keyframes fadeOut {
 56   0% {
 57     opacity: 1;
 58     -webkit-transform: scale(1);
 59             transform: scale(1);
 60   }
 61   100% {
 62     opacity: 0;
 63     -webkit-transform: scale(1.185);
 64             transform: scale(1.185);
 65   }
 66 }
 67 .cm-down-in {
 68   -webkit-animation-name: downIn;
 69           animation-name: downIn;
 70 }
 71 
 72 .cm-down-out {
 73   -webkit-animation-name: downOut;
 74           animation-name: downOut;
 75 }
 76 
 77 @-webkit-keyframes downIn {
 78   0% {
 79     opacity: 0;
 80     -webkit-transform: translate3d(0, 100%, 0);
 81             transform: translate3d(0, 100%, 0);
 82   }
 83   100% {
 84     opacity: 1;
 85     -webkit-transform: translate3d(0, 0, 0);
 86             transform: translate3d(0, 0, 0);
 87   }
 88 }
 89 
 90 @keyframes downIn {
 91   0% {
 92     opacity: 0;
 93     -webkit-transform: translate3d(0, 100%, 0);
 94             transform: translate3d(0, 100%, 0);
 95   }
 96   100% {
 97     opacity: 1;
 98     -webkit-transform: translate3d(0, 0, 0);
 99             transform: translate3d(0, 0, 0);
100   }
101 }
102 @-webkit-keyframes downOut {
103   0% {
104     opacity: 1;
105     -webkit-transform: translate3d(0, 0, 0);
106             transform: translate3d(0, 0, 0);
107   }
108   100% {
109     opacity: 0;
110     -webkit-transform: translate3d(0, 100%, 0);
111             transform: translate3d(0, 100%, 0);
112   }
113 }
114 @keyframes downOut {
115   0% {
116     opacity: 1;
117     -webkit-transform: translate3d(0, 0, 0);
118             transform: translate3d(0, 0, 0);
119   }
120   100% {
121     opacity: 0;
122     -webkit-transform: translate3d(0, 100%, 0);
123             transform: translate3d(0, 100%, 0);
124   }
125 }
126 .cm-up-in {
127   -webkit-animation-name: upIn;
128           animation-name: upIn;
129 }
130 
131 .cm-up-out {
132   -webkit-animation-name: upOut;
133           animation-name: upOut;
134 }
135   </style>
136 </head>
137 <body>
138   <script type="text/javascript">
139     var hasAnimationProperty = function (className) {
140       var animateProprtys = [
141       //有什麼判斷的便新增,暫時只判斷animation,不一樣的動畫特性,判斷方式不一致
142       //        $.fx.cssPrefix + 'transition',
143         $.fx.cssPrefix + 'animation-name'
144       ];
145       var el = $('<div></div>');
146       $('body').append(el);
147 
148       var i, len;
149 
150       //賦予其class
151       el.attr('class', className);
152 
153       for (i = 0, len = animateProprtys.length; i < len; i++) {
154         if (el.css(animateProprtys[i]) != 'none') return true;
155       }
156       s = '';
157       return false;
158     };
159 
160     //false
161     console.log(hasAnimationProperty('test'));
162     //true
163     console.log(hasAnimationProperty('cm-up-out'));
164     //true
165     console.log(hasAnimationProperty('cm-up-in'));
166 
167   </script>
168 </body>
169 </html>
View Code

核心代碼:

 1 var hasAnimationProperty = function (className) {
 2   var animateProprtys = [
 3   //有什麼判斷的便新增,暫時只判斷animation,不一樣的動畫特性,判斷方式不一致
 4   //        $.fx.cssPrefix + 'transition',
 5     $.fx.cssPrefix + 'animation-name'
 6   ];
 7   var el = $('<div></div>');
 8   $('body').append(el);
 9 
10   var i, len;
11 
12   //賦予其class
13   el.attr('class', className);
14 
15   for (i = 0, len = animateProprtys.length; i < len; i++) {
16     if (el.css(animateProprtys[i]) != 'none') return true;
17   }
18   s = '';
19   return false;
20 };
21 
22 //false
23 console.log(hasAnimationProperty('test'));
24 //true
25 console.log(hasAnimationProperty('cm-up-out'));
26 //true
27 console.log(hasAnimationProperty('cm-up-in'));

如此一來,便能判斷該class是否具備樣式屬性了,可是這個代碼還須要擴展,並且這麼也有性能損害,其中涉及到dom操做了,可是想一想動畫形成到gpu負擔,好像也沒什麼問題

相關文章
相關標籤/搜索