# 动画
用JavaScript实现动画,原理非常简单:我们只需要以固定的时间间隔(例如,0.1秒),每次把DOM元素的CSS样式修改一点(例如,高宽各增加10%),看起来就像动画了。
但是要用JavaScript手动实现动画效果,需要编写非常复杂的代码。如果想要把动画效果用函数封装起来便于复用,那考虑的事情就更多了。
使用jQuery实现动画,代码已经简单得不能再简化了:只需要一行代码!
让我们先来看看jQuery内置的几种动画样式:
## show / hide
直接以无参数形式调用`show()`和`hide()`,会显示和隐藏DOM元素。但是,只要传递一个时间参数进去,就变成了动画:
```
var div = $('#test-show-hide');
div.hide(3000); // 在3秒钟内逐渐消失
```
时间以毫秒为单位,但也可以是`'slow'`,`'fast'`这些字符串:
```
var div = $('#test-show-hide');
div.show('slow'); // 在0.6秒钟内逐渐显示
```
`toggle()`方法则根据当前状态决定是`show()`还是`hide()`。
## slideUp / slideDown
你可能已经看出来了,`show()`和`hide()`是从左上角逐渐展开或收缩的,而`slideUp()`和`slideDown()`则是在垂直方向逐渐展开或收缩的。
`slideUp()`把一个可见的DOM元素收起来,效果跟拉上窗帘似的,`slideDown()`相反,而`slideToggle()`则根据元素是否可见来决定下一步动作:
```
var div = $('#test-slide');
div.slideUp(3000); // 在3秒钟内逐渐向上消失
```
## fadeIn / fadeOut
`fadeIn()`和`fadeOut()`的动画效果是淡入淡出,也就是通过不断设置DOM元素的`opacity`属性来实现,而`fadeToggle()`则根据元素是否可见来决定下一步动作:
```
var div = $('#test-fade');
div.fadeOut('slow'); // 在0.6秒内淡出
```
<button class="uk-button" onclick="$('#test-fade').fadeOut('slow');">fadeOut('slow')</button> <button class="uk-button" onclick="$('#test-fade').fadeIn('slow');">fadeIn('slow')</button> <button class="uk-button" onclick="$('#test-fade').fadeToggle('slow');">fadeToggle('slow')</button>
## 自定义动画
如果上述动画效果还不能满足你的要求,那就祭出最后大招:`animate()`,它可以实现任意动画效果,我们需要传入的参数就是DOM元素最终的CSS状态和时间,jQuery在时间段内不断调整CSS直到达到我们设定的值:
```
var div = $('#test-animate');
div.animate({
opacity: 0.25,
width: '256px',
height: '256px'
}, 3000); // 在3秒钟内CSS过渡到设定值
```
`animate()`还可以再传入一个函数,当动画结束时,该函数将被调用:
```
var div = $('#test-animate');
div.animate({
opacity: 0.25,
width: '256px',
height: '256px'
}, 3000, function () {
console.log('动画已结束');
// 恢复至初始状态:
$(this).css('opacity', '1.0').css('width', '128px').css('height', '128px');
});
```
实际上这个回调函数参数对于基本动画也是适用的。
有了`animate()`,你就可以实现各种自定义动画效果了:
## 串行动画
jQuery的动画效果还可以串行执行,通过`delay()`方法还可以实现暂停,这样,我们可以实现更复杂的动画效果,而代码却相当简单:
```
var div = $('#test-animates');
// 动画效果:slideDown - 暂停 - 放大 - 暂停 - 缩小
div.slideDown(2000)
.delay(1000)
.animate({
width: '256px',
height: '256px'
}, 2000)
.delay(1000)
.animate({
width: '128px',
height: '128px'
}, 2000);
}
</script>
```
因为动画需要执行一段时间,所以jQuery必须不断返回新的Promise对象才能后续执行操作。简单地把动画封装在函数中是不够的。
## 为什么有的动画没有效果
你可能会遇到,有的动画如`slideUp()`根本没有效果。这是因为jQuery动画的原理是逐渐改变CSS的值,如`height`从`100px`逐渐变为`0`。但是很多不是block性质的DOM元素,对它们设置`height`根本就不起作用,所以动画也就没有效果。
此外,jQuery也没有实现对`background-color`的动画效果,用`animate()`设置`background-color`也没有效果。这种情况下可以使用CSS3的`transition`实现动画效果。
## 练习
在执行删除操作时,给用户显示一个动画比直接调用`remove()`要更好。请在表格删除一行的时候添加一个淡出的动画效果:
```
'use strict';
function deleteFirstTR() {
var tr = $('#test-table>tbody>tr:visible').first();
}
deleteFirstTR();
```
# AJAX
用JavaScript写AJAX前面已经介绍过了,主要问题就是不同浏览器需要写不同代码,并且状态和错误处理写起来很麻烦。
用jQuery的相关对象来处理AJAX,不但不需要考虑浏览器问题,代码也能大大简化。
## ajax
jQuery在全局对象`jQuery`(也就是`$`)绑定了`ajax()`函数,可以处理AJAX请求。`ajax(url, settings)`函数需要接收一个URL和一个可选的`settings`对象,常用的选项如下:
* async:是否异步执行AJAX请求,默认为`true`,千万不要指定为`false`;
* method:发送的Method,缺省为`'GET'`,可指定为`'POST'`、`'PUT'`等;
* contentType:发送POST请求的格式,默认值为`'application/x-www-form-urlencoded; charset=UTF-8'`,也可以指定为`text/plain`、`application/json`;
* data:发送的数据,可以是字符串、数组或object。如果是GET请求,data将被转换成query附加到URL上,如果是POST请求,根据contentType把data序列化成合适的格式;
* headers:发送的额外的HTTP头,必须是一个object;
* dataType:接收的数据格式,可以指定为`'html'`、`'xml'`、`'json'`、`'text'`等,缺省情况下根据响应的`Content-Type`猜测。
下面的例子发送一个GET请求,并返回一个JSON格式的数据:
```
var jqxhr = $.ajax('/api/categories', {
dataType: 'json'
});
// 请求已经发送了
```
不过,如何用回调函数处理返回的数据和出错时的响应呢?
还记得Promise对象吗?jQuery的jqXHR对象类似一个Promise对象,我们可以用链式写法来处理各种回调:
```
'use strict';
function ajaxLog(s) {
var txt = $('#test-response-text');
txt.val(txt.val() + '\n' + s);
}
$('#test-response-text').val('');
var jqxhr = $.ajax('/api/categories', {
dataType: 'json'
}).done(function (data) {
ajaxLog('成功, 收到的数据: ' + JSON.stringify(data));
}).fail(function (xhr, status) {
ajaxLog('失败: ' + xhr.status + ', 原因: ' + status);
}).always(function () {
ajaxLog('请求完成: 无论成功或失败都会调用');
});
```
## get
对常用的AJAX操作,jQuery提供了一些辅助方法。由于GET请求最常见,所以jQuery提供了`get()`方法,可以这么写:
```
var jqxhr = $.get('/path/to/resource', {
name: 'Bob Lee',
check: 1
});
```
第二个参数如果是object,jQuery自动把它变成query string然后加到URL后面,实际的URL是:
```
/path/to/resource?name=Bob%20Lee&check=1
```
这样我们就不用关心如何用URL编码并构造一个query string了。
## post
`post()`和`get()`类似,但是传入的第二个参数默认被序列化为`application/x-www-form-urlencoded`:
```
var jqxhr = $.post('/path/to/resource', {
name: 'Bob Lee',
check: 1
});
```
实际构造的数据`name=Bob%20Lee&check=1`作为POST的body被发送。
## getJSON
由于JSON用得越来越普遍,所以jQuery也提供了`getJSON()`方法来快速通过GET获取一个JSON对象:
```
var jqxhr = $.getJSON('/path/to/resource', {
name: 'Bob Lee',
check: 1
}).done(function (data) {
// data已经被解析为JSON对象了
});
```
## 安全限制
jQuery的AJAX完全封装的是JavaScript的AJAX操作,所以它的安全限制和前面讲的用JavaScript写AJAX完全一样。
如果需要使用JSONP,可以在`ajax()`中设置`jsonp: 'callback'`,让jQuery实现JSONP跨域加载数据。
关于跨域的设置请参考[浏览器](/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014344997647015f03abc1bb5f46129a7526292a12ab26000) - [AJAX](/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434499861493e7c35be5e0864769a2c06afb4754acc6000)一节中CORS的设置。
- JavaScript教程
- JavaScript简介
- 快速入门
- 基本语法
- 数据类型和变量
- 字符串
- 数组
- 对象
- 条件判断
- 循环
- Map和Set
- iterable
- 函数
- 函数定义和调用
- 变量作用域
- 方法
- 高阶函数
- map/reduce
- filter
- sort
- 闭包
- 箭头函数
- generator
- 标准对象
- Date
- RegExp
- JSON
- 面向对象编程
- 创建对象
- 原型继承
- 浏览器
- 浏览器对象
- 操作DOM
- 更新DOM
- 插入DOM
- 删除DOM
- 操作表单
- 操作文件
- AJAX
- Promise
- Canvas
- jQuery
- 选择器
- 层级选择器
- 查找和过滤
- 操作DOM
- 修改DOM结构
- 事件
- 动画
- 扩展
- underscore
- Collections
- Arrays
- Functions
- Objects
- Chaining
- Node.js
- 安装Node.js和npm
- 第一个Node程序
- 模块
- 基本模块
- fs
- stream
- http
- buffer
- Web开发
- koa
- mysql
- swig
- 自动化工具
- 期末总结
- Python 2.7教程
- Python简介
- 安装Python
- Python解释器
- 第一个Python程序
- 使用文本编辑器
- 输入和输出
- Python基础
- 数据类型和变量
- 字符串和编码
- 使用list和tuple
- 条件判断和循环
- 使用dict和set
- 函数
- 调用函数
- 定义函数
- 函数的参数
- 递归函数
- 高级特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 函数式编程
- 高阶函数
- map/reduce
- filter
- sorted
- 返回函数
- 匿名函数
- 装饰器
- 偏函数
- 模块
- 使用模块
- 安装第三方模块
- 使用__future__
- 面向对象编程
- 类和实例
- 访问限制
- 继承和多态
- 获取对象信息
- 面向对象高级编程
- 使用__slots__
- 使用@property
- 多重继承
- 定制类
- 使用元类
- 错误、调试和测试
- 错误处理
- 调试
- 单元测试
- 文档测试
- IO编程
- 文件读写
- 操作文件和目录
- 序列化
- 进程和线程
- 多进程
- 多线程
- ThreadLocal
- 进程 vs. 线程
- 分布式进程
- 正则表达式
- 常用内建模块
- collections
- base64
- struct
- hashlib
- itertools
- XML
- HTMLParser
- 常用第三方模块
- PIL
- 图形界面
- 网络编程
- TCP/IP简介
- TCP编程
- UDP编程
- 电子邮件
- SMTP发送邮件
- POP3收取邮件
- 访问数据库
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web开发
- HTTP协议简介
- HTML简介
- WSGI接口
- 使用Web框架
- 使用模板
- 协程
- gevent
- 实战
- Day 1 - 搭建开发环境
- Day 2 - 编写数据库模块
- Day 3 - 编写ORM
- Day 4 - 编写Model
- Day 5 - 编写Web框架
- Day 6 - 添加配置文件
- Day 7 - 编写MVC
- Day 8 - 构建前端
- Day 9 - 编写API
- Day 10 - 用户注册和登录
- Day 11 - 编写日志创建页
- Day 12 - 编写日志列表页
- Day 13 - 提升开发效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 编写移动App
- 期末总结
- Python3教程
- Python简介
- 安装Python
- Python解释器
- 第一个Python程序
- 使用文本编辑器
- Python代码运行助手
- 输入和输出
- Python基础
- 数据类型和变量
- 字符串和编码
- 使用list和tuple
- 条件判断
- 循环
- 使用dict和set
- 函数
- 调用函数
- 定义函数
- 函数的参数
- 递归函数
- 高级特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函数式编程
- 高阶函数
- map/reduce
- filter
- sorted
- 返回函数
- 匿名函数
- 装饰器
- 偏函数
- 模块
- 使用模块
- 安装第三方模块
- 面向对象编程
- 类和实例
- 访问限制
- 继承和多态
- 获取对象信息
- 实例属性和类属性
- 面向对象高级编程
- 使用__slots__
- 使用@property
- 多重继承
- 定制类
- 使用枚举类
- 使用元类
- 错误、调试和测试
- 错误处理
- 调试
- 单元测试
- 文档测试
- IO编程
- 文件读写
- StringIO和BytesIO
- 操作文件和目录
- 序列化
- 进程和线程
- 多进程
- 多线程
- ThreadLocal
- 进程 vs. 线程
- 分布式进程
- 正则表达式
- 常用内建模块
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- XML
- HTMLParser
- urllib
- 常用第三方模块
- PIL
- virtualenv
- 图形界面
- 网络编程
- TCP/IP简介
- TCP编程
- UDP编程
- 电子邮件
- SMTP发送邮件
- POP3收取邮件
- 访问数据库
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web开发
- HTTP协议简介
- HTML简介
- WSGI接口
- 使用Web框架
- 使用模板
- 异步IO
- 协程
- asyncio
- async/await
- aiohttp
- 实战
- Day 1 - 搭建开发环境
- Day 2 - 编写Web App骨架
- Day 3 - 编写ORM
- Day 4 - 编写Model
- Day 5 - 编写Web框架
- Day 6 - 编写配置文件
- Day 7 - 编写MVC
- Day 8 - 构建前端
- Day 9 - 编写API
- Day 10 - 用户注册和登录
- Day 11 - 编写日志创建页
- Day 12 - 编写日志列表页
- Day 13 - 提升开发效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 编写移动App
- FAQ
- 期末总结
- Git教程
- Git简介
- Git的诞生
- 集中式vs分布式
- 安装Git
- 创建版本库
- 时光机穿梭
- 版本回退
- 工作区和暂存区
- 管理修改
- 撤销修改
- 删除文件
- 远程仓库
- 添加远程库
- 从远程库克隆
- 分支管理
- 创建与合并分支
- 解决冲突
- 分支管理策略
- Bug分支
- Feature分支
- 多人协作
- 标签管理
- 创建标签
- 操作标签
- 使用GitHub
- 自定义Git
- 忽略特殊文件
- 配置别名
- 搭建Git服务器
- 期末总结