參考資料
前言
本人菜鳥,入IT只為當(dāng)鼓勵(lì)師。本編文章旨在總結(jié)《Javascript高級(jí)層序設(shè)計(jì)(第3版)》一書中第15章canvas的一些api。第(一)篇只總結(jié)2D上下文。
一、<canvan>元素
出處:HTML5。
用途:設(shè)定一個(gè)區(qū)域,通過(guò)JS動(dòng)態(tài)地在區(qū)域上繪制圖形。
組成:由幾組API構(gòu)成,可分為 2D上下文(基本繪圖能力) 和 3D上下文(WebGL)。
兼容性:
- 瀏覽器:IE9+、Firefox1.5+、Safari2+、Opera9+、Chrome、IOS版Safari、Android版WebKit。
- 操作系統(tǒng):Windows XP因缺少必要的繪圖程序,不管用什么瀏覽器都不支持。
二、基本用法
1. 建立區(qū)域
要使用<canvas>元素,必須設(shè)置width和height屬性,指定可以繪圖的區(qū)域大小。設(shè)置完后,如果瀏覽器兼容<canvas>標(biāo)簽,則建立的區(qū)域應(yīng)是空白的;否則,會(huì)顯示其中的文本:A drawing of something。
<canvas id="drawing" width="200" height="200">A drawing of something</canvas>
2. 取得繪圖上下文(<canvas>元素的DOM對(duì)象)
要在這塊畫布上繪圖,需要取得繪圖上下文。繪圖上下文也就是取得相應(yīng)<canvas>元素的DOM對(duì)象。
// 取得DOM對(duì)象,也為canvas對(duì)象
var drawing = document.getElementById("drawing");
3. 取得2D上下文對(duì)象 getContext("2d")
只取得DOM對(duì)象還不行,還要調(diào)用<canvas>DOM對(duì)象擁有的 getContext(param)
方法。其中傳入?yún)?shù)為一個(gè)字符串,因?yàn)楸酒恼驴偨Y(jié)的是2D上下文(平面),故傳入"2d",獲得2D上下文對(duì)象。后面一切關(guān)于繪圖的方法和屬性,其實(shí)都是圍繞著2D上下文對(duì)象開展的,也就是代碼示例中的context。
method | param | return |
---|---|---|
getContext(param) | 上下文對(duì)象的類型,有如下選擇:</br>"2d"(2d上下文)</br>"3d"(3d上下文) | 上下文對(duì)象 |
在調(diào)用該方法之前,為了保證程序的健壯性,最好加入檢測(cè)代碼。
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
// 取得2D上下文對(duì)象
var context = drawing.getContext("2d");
// 更多代碼
}
4. 導(dǎo)出在<canvas>元素上繪制的圖像
當(dāng)在<canvas>元素上繪制完圖像后,可以調(diào)用DOM對(duì)象的 toDataURL(param)
方法,將該圖像導(dǎo)出成為一張圖片,傳入?yún)?shù)為欲導(dǎo)出圖片的MIME類型,例如:image/png
、image/jpeg
等等。擁有該方法的對(duì)象是<canvas>元素的DOM對(duì)象,并不是2d上下文對(duì)象context,兩者要區(qū)分開來(lái)。
method | param | return | compatibility |
---|---|---|---|
toDataURL(param) | 圖像的MIME類型格式,有如下選擇:</br>"image/jpeg"(Firefox和Opera默認(rèn))</br>"image/png"(其他默認(rèn)) | 圖像的數(shù)據(jù)URI | IE9+、Firefox3.5+ 、Opera10 |
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
// 取得圖像的數(shù)據(jù)URI
var imgURI = drawing.toDataURI("image/png");
// 顯示圖像
var image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);
}
三、2D上下文 context
(0, 0)為<canvas>元素所占區(qū)域的原點(diǎn)坐標(biāo),x向右遞增,y向下遞增
1. 填充和描邊
attribute | value | effect |
---|---|---|
fillStyle | String類型:顏色名、十六進(jìn)制碼、rgb等,如"#0000ff" | 設(shè)置填充顏色 |
strokeStyle | String類型:顏色名、十六進(jìn)制碼、rgb等,如"red" | 設(shè)置描邊顏色 |
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 以紅色描邊
context.strokeStyle = "red";
// 以藍(lán)色填充
context.fillStyle = "#0000ff";
// 繪制描邊矩形
context.strokeRect(10, 10, 50, 50);
// 繪制填充矩形 context.fillRect
(65, 10, 50, 50);
}
2. 繪制矩形
2.1 填充矩形
method | param | effect |
---|---|---|
fillRect(param1,param2, param3,param4) | param1:矩形的x坐標(biāo)</br>param2:矩形的y坐標(biāo)</br>param3:矩形的寬度</br>param4:矩形的高度 | 繪制填充矩形,默認(rèn)為黑色,可給fillStyle賦值改變其填充顏色 |
// 填充
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 以紅色填充
context.fillStyle = "#ff0000";
// 繪制填充矩形
context.fillRect(10, 10, 50, 50);
// 以半透明的藍(lán)色填充
context.fillStyle = "rgba(0, 0, 255, 0.5)";
// 繪制填充矩形
context.fillRect(30, 30, 50, 50);
}
2.2 描邊矩形
method | param | effect |
---|---|---|
strokeRect(param1, param2, param3, param4) | param1:矩形的x坐標(biāo)</br>param2:矩形的y坐標(biāo)</br>param3:矩形的寬度</br>param4:矩形的高度 | 繪制描邊矩形,默認(rèn)為黑色,可給strokeStyle賦值改變其邊框顏色 |
// 描邊
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 以紅色描邊
context.strokeStyle = "#ff0000";
// 繪制描邊矩形
context.strokeRect(10, 10, 50, 50);
// 以藍(lán)色描邊
context.strokeStyle = "rgba(0, 0, 255, 0.5)";
// 繪制描邊矩形 context.strokeRect(30, 30, 50, 50);
}
2.3 清除矩形
method | param | effect |
---|---|---|
clearRect(param1, param2, param3, param4) | param1:矩形的x坐標(biāo)</br>param2:矩形的y坐標(biāo)</br>param3:矩形的寬度</br>param4:矩形的高度 | 清除一塊矩形區(qū)域 |
// 清除矩形區(qū)域
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 繪制紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
// 繪制半透明的藍(lán)色矩形
context.fillStyle = "rgba(0, 0, 255, 0.5)";
context.fillRect(30, 30, 50, 50);
// 在兩個(gè)矩形重疊的地方清楚一個(gè)小矩形
context.clearRect(40, 40, 10, 10);
}
3. 繪制路徑
3.1 創(chuàng)建路徑
method | param | effect |
---|---|---|
arc(x,y,radius,startAngle,counterclockwise) | (x, y):圓心的坐標(biāo)</br>radius:弧線半徑</br>startAngle:起始角度</br>endAngle:結(jié)束角度</br>counterclockwise:角度的方向(true:逆時(shí)針?lè)较颍琭alse:順時(shí)針?lè)较?/td> | 給定圓心(x,y)和半徑radius、起始和結(jié)束的角度、方向,繪制一條弧線 |
arcTo(x1,y1,x2,y2,radius) | (x1, y1):需穿過(guò)的點(diǎn)的坐標(biāo)</br>(x2, y2):終點(diǎn)坐標(biāo)</br>radius:以給定的半徑穿過(guò)(x1, y1) | 從上一點(diǎn)繪制一條弧線,到(x2,y2)為止,并且以給定的半徑穿過(guò)(x1, y1) |
bezierCurveTo(c1x,c1y,c2x,c2y,x,y) | (c1x,c1y):控制點(diǎn)</br>(c2x,c2y):另一個(gè)控制點(diǎn)</br>(x, y):終點(diǎn)坐標(biāo) | 從上一點(diǎn)繪制一條弧線,到(x,y)為止,并且以(c1x,c1y)和(c2x,c2y)為控制點(diǎn) |
lineTo(x,y) | (x,y):終點(diǎn)坐標(biāo) | 從上一點(diǎn)開始繪制一條直線,到(x,y)為止 |
moveTo(x,y) | (x,y):繪圖游標(biāo)移動(dòng)到的點(diǎn)的坐標(biāo) | 將繪圖游標(biāo)移動(dòng)到(x,y),不畫線 |
quadraticCurveTo(cx,cy,x,y) | (cx,cy):控制點(diǎn)</br>(x,y):終點(diǎn)坐標(biāo) | 從上一點(diǎn)開始繪制一條二次曲線,到(x,y)為止,并且以(cx,cy)為控制點(diǎn) |
rect(x,y,width,height) | (x,y):起點(diǎn)坐標(biāo)</br>width:矩形的寬</br>height:矩形的高 | 從點(diǎn)(x,y)開始繪制一個(gè)矩形,寬高分別為width和height。這個(gè)方法繪制的是矩形路徑,而不是strokeRect()和fillRect()所繪制的獨(dú)立形狀 |
3.2 處理路徑
method | param | effect |
---|---|---|
beginPath() | null | 開始創(chuàng)建路徑 |
closePath() | null | 關(guān)閉路徑,回到起點(diǎn) |
fill() | null | 用fillStyle來(lái)填充路徑 |
stroke() | null | 用strokeStyle來(lái)對(duì)路徑描邊 |
clip() | null | 在路徑上創(chuàng)建一個(gè)剪切區(qū)域 |
3.2.1 用上面某些方法描繪一個(gè)時(shí)鐘
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 開始路徑
context.beginPath();
// 繪制外圓
context.arc(100, 100, 99, 0, 2*Math.PI, false);
// 繪制內(nèi)圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2*Math.PI, false);
// 繪制分針
context.moveTo(100, 100);
context.lineTo(100, 15);
// 繪制時(shí)針
context.moveTo(100, 100);
context.lineTo(35, 100);
// 為路徑描邊
context.stroke();
}
3.2.2 用fill()方法填充路徑
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 開始路徑
context.beginPath();
// 繪制圓
context.arc(100, 100, 99, 0, 2*Math.PI, false);
// 填充路徑
context.fill();
}
3.2.3 當(dāng)路徑不閉合時(shí)fill()方法無(wú)效
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 繪制一條對(duì)角線
context.moveTo(0, 0);
context.lineTo(200, 200);
// 填充路徑
context.fill();
}
3.3 判斷路徑點(diǎn)
method | param | effect |
---|---|---|
isPointInPath(x,y) | (x,y):某一任意點(diǎn)的坐標(biāo) | 在路徑被關(guān)閉前確定判斷畫布上的點(diǎn)(x,y)是否在路徑上 |
4. 繪制文本
4.1 2d上下文的文本屬性
attribute | value | effect |
---|---|---|
font | String類型,如"bold 14px Arial" | 字體粗細(xì)、大小和樣式 |
textAlign | String類型,有如下選擇:</br>"start"(x坐標(biāo)表示文本左端),"center"(正中),"end"(右端) | 文本的水平對(duì)齊方式 |
textBaseline | String類型,有如下選擇:</br>"top"(y坐標(biāo)表示文本頂端),"middle"(正中),"bottom"(底端),"hanging"、"alphabetic"、"ideographic"(y坐標(biāo)指向特定基線坐標(biāo)) | 文本的垂直對(duì)齊方式 |
其實(shí),fillText()和strokeText()方法都可以接受第四個(gè)參數(shù),表示文本的最大像素寬度,如果傳入字符串大于最大寬度,會(huì)收縮以適應(yīng)最大寬度。但這個(gè)可選參數(shù)尚未得到大多數(shù)瀏覽器的支持。
4.2 用fillText()繪制數(shù)字12
method | param | effect |
---|---|---|
fillText(string,x,y) | string:要繪制的文本字符串</br>x:x坐標(biāo)可選的最大像素寬度</br>y:y坐標(biāo)可選的最大像素寬度 | 使用fillStyle屬性繪制文本 |
strokeText(string,x,y) | string:要繪制的文本字符串</br>x:x坐標(biāo)可選的最大像素寬度</br>y:y坐標(biāo)可選的最大像素寬度 | 使用strokeStyle屬性為文本描邊 |
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if(drawing.getContext) {
var context = drawing.getContext("2d");
// 開始路徑
context.beginPath();
// 繪制外圓
context.arc(100, 100, 99, 0, 2*Math.PI, false);
// 繪制內(nèi)圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2*Math.PI, false);
// 繪制分針
context.moveTo(100, 100);
context.lineTo(100, 15);
// 繪制時(shí)針
context.moveTo(100, 100);
context.lineTo(35, 100);
// 為路徑描邊
context.stroke();
// 繪制數(shù)字12
context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20);
}
</br>
4.3 給定寬度的區(qū)域,找到文本合適的字體大小
method | param | return | effect |
---|---|---|---|
mearsureText(string) | string類型:要確定大小的文本字符串 | object:返回一個(gè)對(duì)象,該對(duì)象只有一個(gè)width屬性 | 獲得存有文本大小的對(duì)象 |
var fontSize = 100,
context.font = fontSize + "px Arial";
if (context.measureText) {
while(context.measureText("Hello world!").width > 140) {
fontSize--;
context.font = fontSize + "px Arial";
}
}
context.fillText("Hello world!", 10, 10);
context.fillText("Font size is " + fontSize + "px", 10, 50);
上面的代碼從100像素的字體大小開始遞減,最終會(huì)找到合適的字體大小,在一個(gè)140像素的矩形區(qū)域中繪制文本 Hello world!
5. 變換
5.1 用translate()方法變換原點(diǎn)位置
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if (drawing.getContext) {
var context = drawing.getContext("2d");
// 開始路徑
context.beginPath();
// 繪制外圓
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
// 繪制內(nèi)圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
// 變換原點(diǎn)
context.translate(100, 100);
// 繪制分針
context.moveTo(0,0);
context.lineTo(0, -85);
// 繪制時(shí)針
context.moveTo(0, 0);
context.lineTo(-65, 0);
// 繪制路徑
context.stroke();
}
可以看出結(jié)果與3.2.1的圖形相同,但把原點(diǎn)變換到時(shí)鐘表盤中心點(diǎn)(100, 100)后,在同一方向上繪制線條的數(shù)學(xué)計(jì)算就變得簡(jiǎn)單起來(lái)了。
5.2 用rotate()方法旋轉(zhuǎn)表針
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if (drawing.getContext) {
var context = drawing.getContext("2d");
// 開始路徑
context.beginPath();
// 繪制外圓
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
// 繪制內(nèi)圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
// 變換原點(diǎn)
context.translate(100, 100);
// 繪制分針
context.moveTo(0,0);
context.lineTo(0, -85);
// 繪制時(shí)針
context.moveTo(0, 0);
context.lineTo(-65, 0);
// 旋轉(zhuǎn)表針,旋轉(zhuǎn)弧度值為1,2π對(duì)應(yīng)360°,則1對(duì)應(yīng)(360/2π)°,約為57.30°
context.rotate(1);
// 繪制路徑
context.stroke();
}
圖中紅色的部分不存在,是我做給大家的提示和比較而已,用來(lái)指明不加最后一句代碼的指針原來(lái)的位置。
5.3 跟蹤上下文的狀態(tài)變化
棧:先進(jìn)后出。
method | param | effect |
---|---|---|
save() | null | 當(dāng)下所有設(shè)置都會(huì)進(jìn)入一個(gè)棧結(jié)構(gòu) |
restore() | null | 在保存設(shè)置的棧結(jié)構(gòu)中向前返回一級(jí) |
var drawing = document.getElementById("drawing");
// 確定瀏覽器支持<canvas>元素
if (drawing.getContext) {
var context = drawing.getContext("2d");
context.fillStyle = "#ff0000";
context.save();
context.fillStyle = "#00ff00";
context.translate(100, 100);
context.save();
context.fillStyle = "#0000ff";
//從點(diǎn)(100,100)開始繪制藍(lán)色矩形
context.fillRect(0,0,100,200);
context.restore();
//從點(diǎn)(110,110)開始繪制綠色矩形
context.fillRect(10,10,100,200);
context.restore();
//從點(diǎn)(0, 0)開始繪制紅色矩形
context.fillRect(0,0,100,200);
}
6. 繪制圖像
把一幅圖像繪制到畫布上,可以使用drawImage()方法。
method | param | effect |
---|---|---|
drawImage(image,</br>tx,ty) | image:要繪制的圖像</br>tx:目標(biāo)圖像的x坐標(biāo)</br>ty:目標(biāo)圖像的y坐標(biāo) | 把圖像繪制到畫布上,起點(diǎn)坐標(biāo)為(tx,ty) |
drawImage(image,</br>tx,ty,twidth,theight) | image:要繪制的圖像</br>tx:目標(biāo)圖像的x坐標(biāo)</br>ty:目標(biāo)圖像的y坐標(biāo)</br>twidth:目標(biāo)圖像的寬度</br>theight:目標(biāo)圖像的高度 | 把圖像繪制到畫布上,起點(diǎn)坐標(biāo)為(tx,ty),繪制到畫布上的圖像的寬度為twidth,高度為theight |
drawImage(image,</br>sx,sy,swidth,sheight,</br>tx,ty,twidth,theight) | image:要繪制的圖像</br>sx:源圖像的x坐標(biāo)</br>sy:源圖像的y坐標(biāo)</br>swidth:源圖像的寬度</br>sheight:源圖像的高度</br>tx:目標(biāo)圖像的x坐標(biāo)</br>ty:目標(biāo)圖像的y坐標(biāo)</br>twidth:目標(biāo)圖像的寬度</br>theight:目標(biāo)圖像的高度 | 先對(duì)源圖像做一些處理,該處理為:以(sx,sy)為起點(diǎn)截取寬度為swidth,高度為sheight的新圖像。處理后再把新圖像繪制到畫布上,起點(diǎn)坐標(biāo)為(tx,ty),繪制到畫布上的新圖像的寬度為twidth,高度為theight |
7. 陰影
attribute | value | effect |
---|---|---|
shadowColor | String類型:顏色名、十六進(jìn)制碼、rgb等,如"#0000ff",默認(rèn)為黑色 | 用CSS顏色格式表示的陰影顏色 |
shadowOffsetX | Number類型,默認(rèn)為0 | 形狀或路徑x軸方向的陰影偏移量 |
shadowOffsetY | Number類型,默認(rèn)為0 | 形狀或路徑y(tǒng)軸方向的陰影偏移量 |
shadowBlur | Number類型,默認(rèn)為0,即不模糊 | 模糊的像素?cái)?shù) |
8. 漸變
method | param | return |
---|---|---|
createLinearGradient | 起點(diǎn)的x坐標(biāo)</br>起點(diǎn)的y坐標(biāo)</br>終點(diǎn)的x坐標(biāo)</br>終點(diǎn)的y坐標(biāo) | 返回CanvasGradient對(duì)象的實(shí)例 |