【编者按】本文主要通过生动的实例,介绍为 Angular 应用添加动画的原理与过程。文章系国内 ITOM 管理平台 OneAPM 编译呈现。

我们知道,Angular 应用在更新 DOM 时,会直接将元素转储为视图而没有过渡,其默认的用户体验并不和谐。

不过,好消息是,Angular 附带了对动画的大力支持;当然,坏消息是它可能和预期效果有所出入。Angular 并不能制作动画,但是为用户的自定义动画提供了许多组件。

理解 $animate 和 ngAnimate 模块

在非 Angular Javascript 应用中更新 DOM 时,程序员会无意识地在动画中加入自定义成分;但是,在 Angular 应用中,经常会使用内置指令,而不是在DOM上直接更改。

因此,开发者要怎么做呢?

如果不使用 Angular,怎样将动画添加到Web应用中呢?

你需要:

  • 定义动画开始和结束的风格;
  • 添加或更改某个元素,并将其设置为起始风格;
  • 设置动画的结束风格;

通常,你会使用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>

这有动画效果,但没有过渡,只是弹出进出。

不借助 $animate 时,为指令添加动画

既然 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类之间的那一行代码会引起回流。如果没有那行,浏览器就不能应用过渡效果,造成元素直接弹出。

现在,终于知道了 $animate 和 ngAnimate 的工作过程

为指令添加动画并不像添加和删除一个类那样简单。你需要知道动画什么时候开始、什么时候结束,开始和结束的状态,知道后需要 JavaScript 协调这一切,这也正是 $animate 的作用内容。

$Animate 服务有添加/删除类和元素的方法。当在指令中使用这些方法时,针对制作动画的元素,Angular 会自动添加和删除类。

它还能在正确的时间添加或删除类,因此你可以自定义开始和结束状态。不仅如此,Angular还能从CSS中读取时间,以便在同一位置定义时间。

重写指令以利用$animate

$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 的内置指令列表,可点击此处

ngAnimate 和 Javascript 动画

你也可以使用 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 也不例外。问题在于,通过 Angular可以创建运行简单的应用程序;但是,在了解它之前,你可能会遇到许多难以检测和调试的问题。这时候,借助 OneAPM 提供的检测工具,就能轻松解决这些难题。

OneAPM Browser Insight 是一个基于真实用户的 Web 前端性能监控平台,能帮助大家定位网站性能瓶颈,实现网站加速效果可视化;支持浏览器、微信、App 浏览 HTML 和 HTML5 页面。想阅读更多技术文章,请访问 OneAPM 官方技术博客

本文转自 OneAPM 官方博客

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

最新文章

  1. Shell编程基础教程6--shell函数
  2. How to generate ssh key only for github and not conflict with original key
  3. Postfix Completion 的使用
  4. IOS开发之SWIFT进阶部分
  5. datagridview数据绑定操作数据库实现增删改查
  6. Spring Boot中使用Swagger2构建API文档
  7. 201521123017 《Java程序设计》第7周学习总结
  8. 201521123028 《Java程序设计》第12周学习总结
  9. iOS 消息发送与转发详解
  10. 如何去掉wordpress后台notice提示窗口
  11. 查看CPU 内存 硬盘 网络 查看进程使用的文件 uptime top ps -aux vmstat iostat iotop nload iptraf nethogs
  12. ganglia监控架构
  13. javafx的scene大小不能在控制器中设置
  14. 推荐一款好用并且免费的markdown软件 Typora
  15. Mybatis简介、环境搭建和详解
  16. 1045 Favorite Color Stripe 动态规划
  17. python命令行参数传递JSON串
  18. 【Java面试题】34 List 、Map、Set 区别?
  19. swift 创建UICollectionView
  20. 2049: [Sdoi2008]Cave 洞穴勘测

热门文章

  1. numpy基本使用2
  2. Selenium自动化测试Python三:WebDriver进阶
  3. iOS事件分发
  4. 玩转mongodb(八):分布式计算--MapReduce
  5. 常见hash原理
  6. [HAOI 2010]订货
  7. jQuery实现两个DropDownList联动(MVC)
  8. list泛型转换成datatable
  9. SQL:多表联合更新
  10. 撩课-Web大前端每天5道面试题-Day35