讓 Angular 應(yīng)用動起來!

【編者按】本文主要通過生動的實例,介紹為 Angular 應(yīng)用添加動畫的原理與過程。文章系國內(nèi) ITOM 管理平臺 OneAPM 編譯呈現(xiàn)。

我們知道,Angular 應(yīng)用在更新 DOM 時,會直接將元素轉(zhuǎn)儲為視圖而沒有過渡,其默認(rèn)的用戶體驗并不和諧。

不過,好消息是,Angular 附帶了對動畫的大力支持;當(dāng)然,壞消息是它可能和預(yù)期效果有所出入。Angular 并不能制作動畫,但是為用戶的自定義動畫提供了許多組件。

理解 $animate 和 ngAnimate 模塊

在非 Angular Javascript 應(yīng)用中更新 DOM 時,程序員會無意識地在動畫中加入自定義成分;但是,在 Angular 應(yīng)用中,經(jīng)常會使用內(nèi)置指令,而不是在DOM上直接更改。

因此,開發(fā)者要怎么做呢?

如果不使用 Angular,怎樣將動畫添加到Web應(yīng)用中呢?
你需要:

  • 定義動畫開始和結(jié)束的風(fēng)格;
  • 添加或更改某個元素,并將其設(shè)置為起始風(fēng)格;
  • 設(shè)置動畫的結(jié)束風(fēng)格;

通常,你會使用Javascript或CSS來完成以上步驟。

當(dāng)往 Angular 應(yīng)用添加動畫時,當(dāng)然也要遵循這個模式,但是卻以 Angular 特有的方式——動畫代碼完全從指令代碼分離出來。

這是很好的方法

Angular 的內(nèi)置指令是預(yù)先為動畫設(shè)定的。這就意味著,你可以使用許多通過 CSS 類或 Javascript 代碼就能調(diào)用的動畫“事件”。這些事件與元素或類的添加/刪除相對應(yīng)。

這可能聽起來有點怪,但其好處是你可以創(chuàng)建自定義指令,然后讓這些指令的終端用戶自定義他們的動畫。

代碼復(fù)用 FTW

這正是 Angular 設(shè)計指令的特有方式。這樣一來,由于 Angular 沒有預(yù)定義動畫,開發(fā)和設(shè)計人員就可以選擇自己喜歡的方式來創(chuàng)建動畫,比如利用CSS過渡/動畫或JavaScript庫。

構(gòu)建自己的指令

如果自己寫一個簡單的自定義指令并做成動畫,更有助于理解各個部分如何協(xié)同工作;然后再回過頭來,更容易理解內(nèi)置指令的工作模式。

下面是一個簡單的指令,旨在無動畫支持時隱藏元素:

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 指令關(guān)注著一個表達(dá)式的取值(本例中 ‘a(chǎn)wesome’ 的值),當(dāng)表達(dá)式判定結(jié)果為真時,在元素中添加類;若為假,則移除類。因為類集顯示設(shè)為 none,所以當(dāng)表達(dá)式為真時 myHide 元素為隱藏狀態(tài)。

<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>

這有動畫效果,但沒有過渡,只是彈出進(jìn)出。

不借助 $animate 時,為指令添加動畫

既然 Angular 動畫只是在關(guān)鍵事件元素中添加CSS類(或通過觸發(fā)Javascript回調(diào)函數(shù),我們稍后將會介紹),再加上Javascript 只能添加或刪除 CSS 類的約束條件,我們可以為指令添加一個簡單的漸淡動畫。因為Javascript 并不了解動畫過程,所以若不定義CSS 類,指令雖然可以執(zhí)行,但不會產(chǎn)生動畫效果。

$animate的工作原理

myHide 動畫能使元素的不透明度從1淡化到0(當(dāng)狀態(tài)切換時則反之)。在動畫結(jié)束時,顯示應(yīng)該設(shè)置為none。

這就有一個有趣的問題,因為只有動畫結(jié)束時才能將顯示設(shè)為none——否則整個動畫運行時,該元素不可見。因此,需要一個CSS類代表過渡/動畫,還需要另一個CSS類,方便在所有事情完成后將顯示設(shè)為none。

到目前為止,CSS 該是什么樣子?

//the final state
.hide {
    display: none;
}
//the animation
.hide-add-start {
    transition: opacity 1s;
    opacity: 0;
}

接著,再在適當(dāng)?shù)臅r候,把指令中的幾行 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 類以便將顯示設(shè)為 none。

用于移除和繪制 hide類動畫的 CSS

. hide-remove {
    transition: opacity 1s;
    opacity: 0;
}
.hide-remove-active {
    opacity: 1;
}

在實現(xiàn)移除 .hide 類的動畫效果時,第一步是將不透明度設(shè)為0;否則,該元素會直接彈出,不存在任何動畫效果。

為了創(chuàng)建不透明度從0到1過渡效果,需要另一個類來定義結(jié)束狀態(tài)。因此,需要一個類來定義起始狀態(tài)和過渡/動畫,另一個類來定義結(jié)束狀態(tài)的動畫。

Javascript 代碼與添加.hide類的過程幾乎一樣,但是現(xiàn)在需要兩個類。

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類之間的那一行代碼會引起回流。如果沒有那行,瀏覽器就不能應(yīng)用過渡效果,造成元素直接彈出。

現(xiàn)在,終于知道了 $animate 和 ngAnimate 的工作過程

為指令添加動畫并不像添加和刪除一個類那樣簡單。你需要知道動畫什么時候開始、什么時候結(jié)束,開始和結(jié)束的狀態(tài),知道后需要 JavaScript 協(xié)調(diào)這一切,這也正是 $animate 的作用內(nèi)容。

$Animate 服務(wù)有添加/刪除類和元素的方法。當(dāng)在指令中使用這些方法時,針對制作動畫的元素,Angular 會自動添加和刪除類。

它還能在正確的時間添加或刪除類,因此你可以自定義開始和結(jié)束狀態(tài)。不僅如此,Angular還能從CSS中讀取時間,以便在同一位置定義時間。

重寫指令以利用$animate

$animate 服務(wù)有幾種用于添加/刪除/移動元素或添加/刪除類的方法。其理念是使用這些方法而不是直接操作DOM,并用 Angular 觸發(fā) Javascript 動畫,或添加/刪除額外需要的CSS類。

你無需加載 ngAnimate 就可以注入 $animate 服務(wù),而且在不觸發(fā)動畫的情況下各個部分都能正常工作。這就太好了,因為即使未定義或使用動畫,你也可以創(chuàng)建正常工作的自定義指令。

如果希望動畫被激活,就必須下載 ng-animate 模塊 Javascript,并把ng-animate 模塊列入你的應(yīng)用程序,如下所示:

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 語句還添加了兩個附加的類:其中一個用于動畫和起始風(fēng)格,另一個用于結(jié)束風(fēng)格。這兩個附加類在結(jié)束時都會被刪除。

添加CSS類需遵循命名約定。因此,在本例中,你添加的類是 “hide” ,則 $animate 會在應(yīng)定義動畫和起始風(fēng)格的位置再添加一個 “hide-add” 類,同時在任意結(jié)束風(fēng)格的位置添加一個 “hide-add-active” 類。

以下是一個說明文檔的截圖,其中說明了需要創(chuàng)建哪些額外的類,命名約定和每個類的添加時間。

讓 Angular 應(yīng)用動起來!

根據(jù)以上規(guī)則,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” 類將顯示值設(shè)為 “block”,因為 “hide” 類在同一時間加入,并設(shè)置顯示為 “none”,而這不是我們想要的。

即使 $animate 指令只能添加一個類,但是它同樣支持 DOM 上用于添加/刪除CSS類的其他操作方法,因此你可以在 Angular 應(yīng)用上實現(xiàn)幾乎所有動畫。

大多數(shù)內(nèi)置指令都使用 $animate 進(jìn)行DOM操作,這意味著你同樣可以為它們實現(xiàn)動畫。若想了解使用 $animate 的內(nèi)置指令列表,可點擊此處

ngAnimate 和 Javascript 動畫

你也可以使用 Javascript 動畫而不是 CSS 動畫/過渡。下面的實例使用了TweenMax 庫,不過你也可以使用其他自己喜歡的庫。

除了添加 CSS 類,$animate 服務(wù)也能觸發(fā)你在 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 服務(wù)和用其進(jìn)行 CSS 動畫的方式一樣,并無區(qū)別。

指令下面是動畫,使用簡單、單一的 CSS 類選擇器來命名。使用該動畫的元素必須包括這個類,否則將無法進(jìn)行動畫操作。

由動畫調(diào)用返回的對象定義了兩個屬性,addClass 和 removeClass。定義這兩個屬性則是因為指令中用到了addClass 和 removeClass。如果元素從指令中移除或添加,則定義為 ‘leave’ 和 ‘enter’ 屬性。

你可以在”由JavaScript定義的動畫”部分查看完整的事件列表。
下面是使用JavaScript動畫的Angular模板。請注意,最終要作動畫的元素中的類,要與Angular應(yīng)用所定義的動畫名稱匹配。

<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>

實現(xiàn)內(nèi)置指令的動畫

大多數(shù)內(nèi)置指令都使用 $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,從而確定調(diào)用 addClass還是removeClass。

再看看其他內(nèi)置指令,就會看到對 $animate的調(diào)用。每個指令的說明文檔記錄了可以在動畫中使用的事件列表。之后,就只是創(chuàng)建CSS動畫還是JavaScript動畫,以及將所有名稱都與命名約定相匹配的問題。

厭倦了 Angular的“魔力”?

Angular的學(xué)習(xí)曲線雖然并不簡單,但歸根結(jié)底還是值得我們學(xué)習(xí)的。不過, Angular 充滿了奇怪的新概念,而且最終的結(jié)果有時看起來簡直不可思議。

所有的框架都堅持己見,Angular 也不例外。問題在于,通過 Angular可以創(chuàng)建運行簡單的應(yīng)用程序;但是,在了解它之前,你可能會遇到許多難以檢測和調(diào)試的問題。這時候,借助 OneAPM 提供的檢測工具,就能輕松解決這些難題。

OneAPM Browser Insight 是一個基于真實用戶的 Web 前端性能監(jiān)控平臺,能幫助大家定位網(wǎng)站性能瓶頸,實現(xiàn)網(wǎng)站加速效果可視化;支持瀏覽器、微信、App 瀏覽 HTML 和 HTML5 頁面。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方技術(shù)博客。

本文轉(zhuǎn)自 OneAPM 官方博客

原文鏈接:http://www.planningforaliens.com/angular/animate-your-angular-application/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ng-model 指令ng-model 指令 綁定 HTML 元素 到應(yīng)用程序數(shù)據(jù)。ng-model 指令也可以:...
    壬萬er閱讀 974評論 0 2
  • 通過jQuery,您可以選?。ú樵儯琿uery)HTML元素,并對它們執(zhí)行“操作”(actions)。 jQuer...
    枇杷樹8824閱讀 724評論 0 3
  • AngularJS是什么?AngularJs(后面就簡稱ng了)是一個用于設(shè)計動態(tài)web應(yīng)用的結(jié)構(gòu)框架。首先,它是...
    200813閱讀 1,792評論 0 3
  • 有時候,感覺人真的是一個奇妙的生物,上一刻你還在悲傷,下一刻你卻是由于獲得了一些滿足而變得激情昂揚。 生活中的很多...
    Do_yourself閱讀 1,051評論 0 0
  • 人類在為自己省力方面,真的可以說孜孜不倦。就為了更快更舒適的移動這件事而言,從最早用騎馬來代步,到后來馬車的發(fā)明,...
    叁菇?jīng)?/span>閱讀 296評論 0 0

友情鏈接更多精彩內(nèi)容