【编者按】本文主要通过生动的实例,介绍为 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/

让 Angular 应用动起来!的更多相关文章

  1. Css3动画库收集

    1.animate.css – 齐全的CSS3动画库 http://www.dowebok.com/98.html 2.Angular官方动画库 http://augus.github.io/ngAn ...

  2. angular smart-table组件如何定制化之初步研究

    table表运用在后台管理用得频繁,一般bootstrap的class="table"就能满足样式需求以及分页需求,但是每个产品经理需求不一样,比如说分页:bootstrap分页实 ...

  3. 开始学习Angular Mobile UI

    介绍 Mobile AngularUI 可以让你使用Twitter Booostrap和Angular JS来开发混合移动App和桌面应用程序. 下面是是一些贯穿整个项目的步骤,我强烈的建议你去继续阅 ...

  4. 转:angular的decorator方法

    AngularJS实例 – 装饰$log 在AngularJS中,我们可以使用Angular内置或者自定义的services,在应用的各个部分之间分享数据和方法.假设你已经定义了一个service,但 ...

  5. soket.io.js + angular.js + express.js(node.js)

    soket.io.js + angular.js + express.js(node.js) 今天搭建个soket.io.js + angular.js + express.js的环境, 采坑无数,特 ...

  6. Angular React 和 Vue的比较

    Angular(1&2),React,Vue对比 一 数据流 数据绑定 Angular 使用双向绑定即:界面的操作能实时反映到数据,数据的变更能实时展现到界面. 实现原理: $scope变量中 ...

  7. angular 2 animate 笔记

    好久没有在这里写点笔记了.时隔已久,angular1 去 angular2 咯 笔记来源:https://angular.cn/docs/ts/latest/guide/animations.html ...

  8. angular自动化测试--protractor

    前戏 面向模型编程: 测试驱动开发: 先保障交互逻辑,再调整细节.---by 雪狼. 为什么要自动化测试? 1,提高产出质量. 2,减少重构时的痛.反正我最近重构多了,痛苦经历多了. 3,便于新人接手 ...

  9. 迈向angularjs2系列(8):angular cli和angular2种子项目

    文章目录 1.angular cli快速搭建项目 2.angular2-seed 3.手动配置 题外话:如何更好的阅读本篇文章 一: angular cli的安装 Angular-cli(命令行界面, ...

随机推荐

  1. Python -----issubclass和isinstance

    issubclass用于判断一个类是否为另一个类的子类,isinstance用于判断一个对象是否某类的一个实例 import math class Point: def __init__(self, ...

  2. Android四大组件之一 -- Service详解

    相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序 ...

  3. Python学习--18 进程和线程

    线程是最小的执行单元,而进程由至少一个线程组成.如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间. 进程 fork调用 通过fork()系统调用,就可以生成一个子进程 ...

  4. CentOS7 下编译安装 Samba,什么是 SMB/CIFS 协议

    目录 一.关于 Samba 1. SMB 2. Samba 二.yum 安装 Samba 1. 安装 Samba 2. 查看版本 3. 查看配置文件 4. 启动服务 5. 本地客户端验证 6. Win ...

  5. ASP.NET MVC 表格操作

    Beginners Guide for Creating GridView in ASP.NET MVC 5 http://www.codeproject.com/Articles/1114208/B ...

  6. Java 容器源码分析之Queue

    简介 Queue是一种很常见的数据结构类型,在java里面Queue是一个接口,它只是定义了一个基本的Queue应该有哪些功能规约.实际上有多个Queue的实现,有的是采用线性表实现,有的基于链表实现 ...

  7. CentOS QT can't find lGL

    直接安装: yum install libGL, yum install libGL-devel 库即可.

  8. 疯狂Java讲义PDF

    java学习资料,仅供学习交流,自行取用↓ 链接:https://pan.baidu.com/s/1dF1wCST 密码:i75g

  9. Win10 注册IIs4.0的解决方案

    随着Win10的出现,越来越多的人装上了Win10,尤其是程序员,由于Win10是一个新的操作系统,但现有软件的兼容性等各方面都是未知,难免会存在很多坑,就拿IIS来说,我刚装完win10系统,然后装 ...

  10. Ubuntu 18.04 安装 Docker-ce(就是Docker社区版本)

    一步都不用改,跟着走就行 1.更换国内软件源,推荐中国科技大学的源,稳定速度快(可选) sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak ...