让 Angular 应用动起来!
【编者按】本文主要通过生动的实例,介绍为 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 应用动起来!的更多相关文章
- Css3动画库收集
1.animate.css – 齐全的CSS3动画库 http://www.dowebok.com/98.html 2.Angular官方动画库 http://augus.github.io/ngAn ...
- angular smart-table组件如何定制化之初步研究
table表运用在后台管理用得频繁,一般bootstrap的class="table"就能满足样式需求以及分页需求,但是每个产品经理需求不一样,比如说分页:bootstrap分页实 ...
- 开始学习Angular Mobile UI
介绍 Mobile AngularUI 可以让你使用Twitter Booostrap和Angular JS来开发混合移动App和桌面应用程序. 下面是是一些贯穿整个项目的步骤,我强烈的建议你去继续阅 ...
- 转:angular的decorator方法
AngularJS实例 – 装饰$log 在AngularJS中,我们可以使用Angular内置或者自定义的services,在应用的各个部分之间分享数据和方法.假设你已经定义了一个service,但 ...
- 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的环境, 采坑无数,特 ...
- Angular React 和 Vue的比较
Angular(1&2),React,Vue对比 一 数据流 数据绑定 Angular 使用双向绑定即:界面的操作能实时反映到数据,数据的变更能实时展现到界面. 实现原理: $scope变量中 ...
- angular 2 animate 笔记
好久没有在这里写点笔记了.时隔已久,angular1 去 angular2 咯 笔记来源:https://angular.cn/docs/ts/latest/guide/animations.html ...
- angular自动化测试--protractor
前戏 面向模型编程: 测试驱动开发: 先保障交互逻辑,再调整细节.---by 雪狼. 为什么要自动化测试? 1,提高产出质量. 2,减少重构时的痛.反正我最近重构多了,痛苦经历多了. 3,便于新人接手 ...
- 迈向angularjs2系列(8):angular cli和angular2种子项目
文章目录 1.angular cli快速搭建项目 2.angular2-seed 3.手动配置 题外话:如何更好的阅读本篇文章 一: angular cli的安装 Angular-cli(命令行界面, ...
随机推荐
- Git for Windows之分支管理、分支合并、解决分支冲突
分支是在稳定版本出现bug的情况下,通过分支技术,在保证稳定版本稳定的情况,修改稳定版本的(差异下载的,速度极快,不同于SVN等技术,会将所有的代码下载到本地)副本,通过合并,解决冲突,最后回归到稳定 ...
- 广度优先遍历-BFS、深度优先遍历-DFS
广度优先遍历-BFS 广度优先遍历类似与二叉树的层序遍历算法,它的基本思想是:首先访问起始顶点v,接着由v出发,依次访问v的各个未访问的顶点w1 w2 w3....wn,然后再依次访问w1 w2 w3 ...
- JAVA面试精选【Java基础第三部分】
上一篇,我们给出了大概35个题目,都是基础知识,有童鞋反映题目过时了,其实不然,这些是基础中的基础,但是也是必不可少的,面试题目中还是有一些基础题目的,我们本着先易后难的原则,逐渐给出不同级别的题目, ...
- CentOS 7 安装配置 Gitlab
centos:http://www.centos.org/download/ download:https://about.gitlab.com/downloads/ update:https://g ...
- 对称加密-AES
假设有一个发送方在向接收方发送消息.如果没有任何加密算法,接收方发送的是一个明文消息:“我是小灰”. 如果消息被中间人截获到,即使中间人无法篡改消息,也可以窥探到消息的内容,从而暴露了通信双方的私密. ...
- Percona XtraDB Cluster
简介 Percona XtraDB Cluster是MySQL高可用性和可扩展性的解决方案,Percona XtraDB Cluster提供的特性如下: 1.同步复制,事务要么在所有节点提交或不提交. ...
- (转)mybatis-plus入门
目前正在维护的公司的一个项目是一个ssm架构的java项目,dao层的接口有大量数据库查询的方法,一个条件变化就要对应一个方法,再加上一些通用的curd方法,对应一张表的dao层方法有时候多达近20个 ...
- rails中accepts_nested_attributes_for应用
Model: class Blog < ActiveRecord::Base has_many :strip_rules accepts_nested_attributes_for :strip ...
- 25. k个一组翻转链表
题目描述 给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表. k 是一个正整数,它的值小于或等于链表的长度.如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序. 示例 : 给定 ...
- laravel 文件上传
laravel 文件上传 先开扩展 表单中能够选择图片 数据处理C层, 接图片并保存 保存图片: 设置目录 store()的第一个参数说明: 存放图片的子目录. 如何获取文件的类型 大小: $uplo ...