【編者按】本文主要經過生動的實例,介紹爲 Angular 應用添加動畫的原理與過程。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。html
咱們知道,Angular 應用在更新 DOM 時,會直接將元素轉儲爲視圖而沒有過渡,其默認的用戶體驗並不和諧。前端
不過,好消息是,Angular 附帶了對動畫的大力支持;固然,壞消息是它可能和預期效果有所出入。Angular 並不能製做動畫,可是爲用戶的自定義動畫提供了許多組件。程序員
在非 Angular Javascript 應用中更新 DOM 時,程序員會無心識地在動畫中加入自定義成分;可是,在 Angular 應用中,常常會使用內置指令,而不是在DOM上直接更改。angularjs
所以,開發者要怎麼作呢?api
若是不使用 Angular,怎樣將動畫添加到Web應用中呢?
你須要:瀏覽器
定義動畫開始和結束的風格;微信
添加或更改某個元素,並將其設置爲起始風格;app
設置動畫的結束風格;框架
一般,你會使用Javascript或CSS來完成以上步驟。前端性能
當往 Angular 應用添加動畫時,固然也要遵循這個模式,可是卻以 Angular 特有的方式——動畫代碼徹底從指令代碼分離出來。
這是很好的方法
Angular 的內置指令是預先爲動畫設定的。這就意味着,你可使用許多經過 CSS 類或 Javascript 代碼就能調用的動畫「事件」。這些事件與元素或類的添加/刪除相對應。
這可能聽起來有點怪,但其好處是你能夠建立自定義指令,而後讓這些指令的終端用戶自定義他們的動畫。
代碼複用 FTW
這正是 Angular 設計指令的特有方式。這樣一來,因爲 Angular 沒有預約義動畫,開發和設計人員就能夠選擇本身喜歡的方式來建立動畫,好比利用CSS過渡/動畫或JavaScript庫。
若是本身寫一個簡單的自定義指令並作成動畫,更有助於理解各個部分如何協同工做;而後再回過頭來,更容易理解內置指令的工做模式。
下面是一個簡單的指令,旨在無動畫支持時隱藏元素:
app.controller("example", function($scope){ $scope.awesome = false; }); app.directive("myHide", function(){ return { restrict: 'A', link: function(scope, elem, attrs){ scope.$watch(attrs.myHide, function(value){ if (value) { elem.addClass("hide"); } else { elem.removeClass("hide"); } }); } }; });
myHide 指令關注着一個表達式的取值(本例中 ‘awesome’ 的值),當表達式斷定結果爲真時,在元素中添加類;若爲假,則移除類。由於類集顯示設爲 none,因此當表達式爲真時 myHide 元素爲隱藏狀態。
<div class="myHideExample" ng-controller="example"> <div class="message"> <p my-hide="awesome">Hide this text if awesome</p> </div> <button class="button" ng-click="awesome = !awesome">Toggle awesomeness</button> </div>
這有動畫效果,但沒有過渡,只是彈出進出。
既然 Angular 動畫只是在關鍵事件元素中添加CSS類(或經過觸發Javascript回調函數,咱們稍後將會介紹),再加上Javascript 只能添加或刪除 CSS 類的約束條件,咱們能夠爲指令添加一個簡單的漸淡動畫。由於Javascript 並不瞭解動畫過程,因此若不定義CSS 類,指令雖然能夠執行,但不會產生動畫效果。
$animate的工做原理
myHide 動畫能使元素的不透明度從1淡化到0(當狀態切換時則反之)。在動畫結束時,顯示應該設置爲none。
這就有一個有趣的問題,由於只有動畫結束時才能將顯示設爲none——不然整個動畫運行時,該元素不可見。所以,須要一個CSS類表明過渡/動畫,還須要另外一個CSS類,方便在全部事情完成後將顯示設爲none。
到目前爲止,CSS 該是什麼樣子?
//the final state .hide { display: none; } //the animation .hide-add-start { transition: opacity 1s; opacity: 0; }
接着,再在適當的時候,把指令中的幾行 Javascript 語句加入到類中。
/ add this first to start the animation elem.addClass("hide-add-start"); setTimeout(function() { // add the hide class after animation is finished elem.addClass("hide"); // clean up elem.removeClass("hide-add-start"); }, 1000);
因此 .hide-add-start 類添加了過渡效果和最終值,過渡完成以後再添加 .hide 類以便將顯示設爲 none。
用於移除和繪製 hide類動畫的 CSS
. hide-remove { transition: opacity 1s; opacity: 0; } .hide-remove-active { opacity: 1; }
在實現移除 .hide 類的動畫效果時,第一步是將不透明度設爲0;不然,該元素會直接彈出,不存在任何動畫效果。
爲了建立不透明度從0到1過渡效果,須要另外一個類來定義結束狀態。所以,須要一個類來定義起始狀態和過渡/動畫,另外一個類來定義結束狀態的動畫。
Javascript 代碼與添加.hide類的過程幾乎同樣,可是如今須要兩個類。
elem.addClass("hide-remove"); elem.removeClass("hide"); // cause a reflow elem[0].offsetHeight; elem.addClass("hide-remove-active"); setTimeout(function(){ elem.removeClass("hide-remove"); elem.removeClass("hide-remove-active"); }, 1000);
移除.hide類和添加.hide-remove-active類之間的那一行代碼會引發迴流。若是沒有那行,瀏覽器就不能應用過渡效果,形成元素直接彈出。
爲指令添加動畫並不像添加和刪除一個類那樣簡單。你須要知道動畫何時開始、何時結束,開始和結束的狀態,知道後須要 JavaScript 協調這一切,這也正是 $animate 的做用內容。
$Animate 服務有添加/刪除類和元素的方法。當在指令中使用這些方法時,針對製做動畫的元素,Angular 會自動添加和刪除類。
它還能在正確的時間添加或刪除類,所以你能夠自定義開始和結束狀態。不只如此,Angular還能從CSS中讀取時間,以便在同一位置定義時間。
$animate 服務有幾種用於添加/刪除/移動元素或添加/刪除類的方法。其理念是使用這些方法而不是直接操做DOM,並用 Angular 觸發 Javascript 動畫,或添加/刪除額外須要的CSS類。
你無需加載 ngAnimate 就能夠注入 $animate 服務,並且在不觸發動畫的狀況下各個部分都能正常工做。這就太好了,由於即便未定義或使用動畫,你也能夠建立正常工做的自定義指令。
若是但願動畫被激活,就必須下載 ng-animate 模塊 Javascript,並把ng-animate 模塊列入你的應用程序,以下所示:
var app = angular.module('animations', ['ngAnimate']);
有了 $animate,myHide 指令的新版本以下所示:
app.directive("myHide", function($animate){ return { restrict: 'A', link: function(scope, elem, attrs){ scope.$watch(attrs.myHide, function(value){ if (value) { $animate.addClass(elem, "hide"); } else { $animate.removeClass(elem, "hide"); } }); } }; });
CSS將略有不一樣。除了要添加到元素中的實際的類,addClass 和 removeClass 語句還添加了兩個附加的類:其中一個用於動畫和起始風格,另外一個用於結束風格。這兩個附加類在結束時都會被刪除。
添加CSS類需遵循命名約定。所以,在本例中,你添加的類是 「hide」 ,則 $animate 會在應定義動畫和起始風格的位置再添加一個 「hide-add」 類,同時在任意結束風格的位置添加一個 「hide-add-active」 類。
如下是一個說明文檔的截圖,其中說明了須要建立哪些額外的類,命名約定和每一個類的添加時間。
根據以上規則,CSS 可以下所示:
. .hide-add { display: block; transition: opacity 1s; opacity: 1; } .hide-add-active { opacity: 0; } .hide-remove { transition: opacity 1s; display: block; opacity: 0; } .hide-remove-active { opacity: 1; }
「hide-add」 類將顯示值設爲 「block」,由於 「hide」 類在同一時間加入,並設置顯示爲 「none」,而這不是咱們想要的。
即便 $animate 指令只能添加一個類,可是它一樣支持 DOM 上用於添加/刪除CSS類的其餘操做方法,所以你能夠在 Angular 應用上實現幾乎全部動畫。
大多數內置指令都使用 $animate
進行DOM操做,這意味着你一樣能夠爲它們實現動畫。若想了解使用 $animate
的內置指令列表,可點擊此處。
你也可使用 Javascript 動畫而不是 CSS 動畫/過渡。下面的實例使用了TweenMax 庫,不過你也可使用其餘本身喜歡的庫。
除了添加 CSS 類,$animate 服務也能觸發你在 APP 中定義的任何JavaScript動畫。
app.directive("myHideJs", function($animate){ return { restrict: 'A', link: function(scope, elem, attrs){ scope.$watch(attrs.myHideJs, function(value){ if (value) { $animate.addClass(elem, "hide-js"); } else { $animate.removeClass(elem, "hide-js"); } }); } }; }); app.animation('.hide-js-animated', function(){ return { addClass: function(element, className){ TweenMax.to(element, 1, { 'opacity': 0 }); }, removeClass: function(element, className){ TweenMax.to(element, 1, { 'opacity': 1 }); } } });
能夠看到,在該指令使用 $animate 服務和用其進行 CSS 動畫的方式同樣,並沒有區別。
指令下面是動畫,使用簡單、單一的 CSS 類選擇器來命名。使用該動畫的元素必須包括這個類,不然將沒法進行動畫操做。
由動畫調用返回的對象定義了兩個屬性,addClass 和 removeClass。定義這兩個屬性則是由於指令中用到了addClass 和 removeClass。若是元素從指令中移除或添加,則定義爲 ‘leave’ 和 ‘enter’ 屬性。
你能夠在」由JavaScript定義的動畫」部分查看完整的事件列表。
下面是使用JavaScript動畫的Angular模板。請注意,最終要做動畫的元素中的類,要與Angular應用所定義的動畫名稱匹配。
<div class="myHideExample" ng-controller="example"> <div class="message"> <p my-hide-js="awesome" class="hide-js-animated">Hide this text if awesome</p> </div> <button class="button" ng-click="awesome = !awesome">Toggle awesomeness</button> </div>
大多數內置指令都使用 $animate,正如 myHide指令。下面爲ngHide代碼:
var ngHideDirective = ['$animate', function($animate) { return { restrict: 'A', multiElement: true, link: function(scope, element, attr) { scope.$watch(attr.ngHide, function ngHideWatchAction(value) { // The comment inside of the ngShowDirective explains why we add and // remove a temporary class for the show/hide animation $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, { tempClasses: NG_HIDE_IN_PROGRESS_CLASS }); }); } }; }];
是否是很眼熟?這是由於它幾乎和你這段時間一直在看的 myHide 指令徹底同樣。不過也有少量不一樣,主要是ngHide使用三元運算符來代替 if / else,從而肯定調用 addClass仍是removeClass。
再看看其餘內置指令,就會看到對 $animate的調用。每一個指令的說明文檔記錄了能夠在動畫中使用的事件列表。以後,就只是建立CSS動畫仍是JavaScript動畫,以及將全部名稱都與命名約定相匹配的問題。
Angular的學習曲線雖然並不簡單,但歸根結底仍是值得咱們學習的。不過, Angular 充滿了奇怪的新概念,並且最終的結果有時看起來簡直難以想象。
全部的框架都堅持己見,Angular 也不例外。問題在於,經過 Angular能夠建立運行簡單的應用程序;可是,在瞭解它以前,你可能會遇到許多難以檢測和調試的問題。這時候,藉助 OneAPM 提供的檢測工具,就能輕鬆解決這些難題。
OneAPM Browser Insight 是一個基於真實用戶的 Web 前端性能監控平臺,能幫助你們定位網站性能瓶頸,實現網站加速效果可視化;支持瀏覽器、微信、App 瀏覽 HTML 和 HTML5 頁面。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。
本文轉自 OneAPM 官方博客
原文連接:http://www.planningforaliens.com/angular/animate-your-angular-application/