# 動畫
用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服務器
- 期末總結