Menu
第15章 使用 Canvas 繪圖
15.1 基本用法
15.2 2D 上下文
- 15.2.1 填充和描邊
- 15.2.2 繪制矩形
- 15.2.3 繪制路徑
- 15.2.4 繪制文本
- 15.2.5 變換
- 15.2.6 繪制圖像
- 15.2.7 陰影
- 15.2.8 漸變
- 15.2.9 模式
- 15.2.10 使用圖像數據
- 15.2.11 合成
15.1 基本用法
- HTML里添加<canvas>元素:接著必須先設置其 width 和 height 屬性,指定可以繪圖的區域大小。出現在開始和結束標簽中的內容是后備信息,如果瀏覽器不支持<canvas>元素,就會顯示這些信息。下面就是<canvas>元素的例子。
<canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas>
- 取得繪圖上下文:要在這塊畫布(canvas)上繪圖,需要取得繪圖上下文。而取得繪圖上下文對象的引用,需要調用getContext()方法并傳入上下文的名字。傳入"2d",就可以取得 2D 上下文對象。
var drawing = document.getElementById("drawing");
// 取得 2D 上下文對象
var context = drawing.getContext("2d");
- 導出在<canvas>元素上繪制的圖像:使用 toDataURL()方法,這個方法接受一個參數,即圖像的 MIME
類型格式,而且適合用于創建圖像的任何上下文。默認情況下,瀏覽器會將圖像編碼為 PNG 格式(除非另行指定
)。比如,要取得畫布中的一幅 PNG 格式的圖像。:
var drawing = document.getElementById("drawing");
//取得圖像的數據 URI
var imgURI = drawing.toDataURL("image/png");
//顯示圖像
var image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);
15.2 2D 上下文
- 2D 上下文的坐標開始于<canvas>元素的左上角,原點坐標是(0,0)。
- 15.2.1 填充和描邊
- 2D 上下文的兩種基本繪圖操作是填充和描邊。填充,就是用指定的樣式(顏色、漸變或圖像)填
充圖形;描邊,就是只在圖形的邊緣畫線。操作的結果取決于兩個屬性: fillStyle 和 strokeStyle。 - 這兩個屬性的值可以是字符串、漸變對象或模式對象,而且它們的默認值都是"#000000"。如果為它們指定表示顏色的字符串值,可以使用 CSS 中指定顏色值的任何格式,包括顏色名、十六進制碼、rgb、 rgba、 hsl 或 hsla。舉個例子:
var drawing = document.getElementById("drawing");
var context = drawing.getContext("2d");
// 所有涉及描邊和填充的操作都將使用這兩個樣式,填充和描邊的顏色需要寫在位置前面;
context.strokeStyle = "red";
context.fillStyle = "#0000ff"
15.2.2 繪制矩形
-
矩形是唯一一種可以直接在 2D 上下文中繪制的形狀。與矩形有關的方法包括 fillRect()、strokeRect()和 clearRect()。這三個方法都能接收 4 個參數:矩形的 x 坐標、矩形的 y 坐標、矩形寬度和矩形高度。這些參數的單位都是像素。
- fillRect():方法在畫布上繪制的矩形,默認填充黑色;
- fillStyle:屬性指定填充的顏色。可以使用 CSS 中指定顏色值的任何格式,包括顏色名、十六進制碼、rgb、 rgba、 hsl 或 hsla。
//繪制紅色矩形
context.fillStyle = "#ff0000";
// (x, y, width, height)
context.fillRect(10, 10, 50, 50);
//繪制半透明的藍色矩形
context.fillStyle = "rgba(0,0,255,0.5)";
context.fillRect(30, 30, 50, 50);
- strokeStyle屬性:描邊顏色通過 strokeStyle 屬性指定。
- strokeRect()方法:方法在畫布上繪制的矩形描邊,默認填充黑色;
- lineWidth屬性:描邊線條的寬度由 lineWidth 屬性控制,該屬性的值可以是任意整數;
- lineCap 屬性:可以控制線條末端的形狀是平頭、圓頭還是方頭("butt"、"round"或"square"),
- lineJoin 屬性:可以控制線條相交的方式是圓交、斜交還是斜接("round"、 "bevel"或"miter")。
//繪制紅色描邊矩形
context.strokeStyle = "#ff0000";
context.strokeRect(10, 10, 50, 50);
//繪制半透明的藍色描邊矩形
context.strokeStyle = "rgba(0,0,255,0.5)";
context.strokeRect(30, 30, 50, 50);
// 以上代碼繪制了兩個重疊的矩形。不過,這兩個矩形都只有框線,內部并沒有填充顏色
- clearRect()方法:用于清除畫布上的矩形區域。這個方法可以把繪制上下文中的某一矩形區域變透明。
ctx.fillRect(0, 0, 100, 100);
ctx.clearRect(30, 30, 40, 40)
以上code效果
以上code效果
- 15.2.3 繪制路徑
- beginPath()方法:開始繪制新路徑。
- arc(x, y, radius, startAngle, endAngle, counterclockwise):繪制圓形;參數如下:
- x:圓弧中心(圓心)的 x 軸坐標。
- y:圓弧中心(圓心)的 y 軸坐標。
- radius:圓弧的半徑。
- startAngle:圓弧的起始點, x軸方向開始計算,單位以弧度表示。
- endAngle:圓弧的終點, 單位以弧度表示。(0, 2*Math.PI 表示畫整個圓)
- anticlockwise :可選的Boolean值 ,如果為 true,逆時針繪制圓弧,反之,順時針繪制。
canvas = document.getElementById("drawing");
context = canvas.getContext("2d"); // 得到畫圖板對象; ?
context.beginPath(); // 創建新路徑;
context.strokeStyle = "rgba(255,0,255,0.66)";
context.lineWidth = 3;
context.arc(250, 250, 100, 2*Math.PI, false); // 弧度方法;
context.stroke();
- lineTo(x, y):從上一點開始繪制一條直線,到(x,y)為止。
context.moveTo(250,250);
context.lineTo(250,160);
context.stroke();
- moveTo(x, y):將繪圖游標移動到(x,y),不畫線。
- arcTo(x1, y1, x2, y2, radius):從上一點開始繪制一條弧線,到(x2,y2)為止,并且以給定的半徑 radius 穿過(x1,y1)。(是 Canvas 2D API 根據控制點和半徑繪制圓弧路徑,使用當前的描點(前一個moveTo或lineTo等函數的止點)。根據當前描點與給定的控制點1連接的直線,和控制點1與控制點2連接的直線,作為使用指定半徑的圓的切線,畫出兩條切線之間的弧線路徑。)
- bezierCurveTo(c1x, c1y, c2x, c2y, x, y):貝塞爾曲線, 從上一點開始繪制一條曲線,到(x,y)為止,并且以(c1x,c1y)和(c2x,c2y)為控制點。
var c=document.getElementById("drawing");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(50,250);
ctx.bezierCurveTo(200,150,300,350,450,250);
// cp1x 第一個貝塞爾控制點的 +
x 坐標
// cp1y 第一個貝塞爾控制點的 y 坐標
// cp2x 第二個貝塞爾控制點的 x 坐標
// cp2y 第二個貝塞爾控制點的 y 坐標
// x 結束點的 x 坐標
// y 結束點的 y 坐標
- quadraticCurveTo(cx, cy, x, y):從上一點開始繪制一條二次曲線,到(x,y)為止,并且以(cx,cy)作為控制點。
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d"); // 創建canvas的執行環境;
context.beginPath(); // 創建畫圖路徑;
context.moveTo(50,250);
context.quadraticCurveTo(250, 450,450, 250); // 一個控制點的線;
context.stroke() // 筆畫描邊;
- rect(x, y, width, height):從點(x,y)開始繪制一個矩形,寬度和高度分別由 width 和height 指定。這個方法繪制的是矩形路徑,而不是 strokeRect()和 fillRect()所繪制的獨立的形狀。
context.beginPath(); // 創建畫圖路徑;
context.rect(50,50,100,65);
context.stroke() // 筆畫描邊;
- closePath():繪制一條連接到路徑起點的線條;
context.beginPath(); // 創建畫圖路徑;
context.moveTo(50,250);
context.quadraticCurveTo(250, 450,450, 250); // 一個控制點的線;
context.closePath();
context.stroke(); // 筆畫描邊;
closePath()效果
- fill():用 fillStyle 填充路徑。
- stroke():用strokeStyle描邊路徑。
- clip() 方法從原始畫布中剪切任意形狀和尺寸。一旦剪切了某個區域,則所有之后的繪圖都會被限制在被剪切的區域內(不能訪問畫布上的其他區域)。您也可以在使用 clip() 方法前通過使用 save() 方法對當前畫布區域進行保存,并在以后的任意時間對其進行恢復(通過 restore() 方法)。
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
// 剪切矩形區域
ctx.rect(50,20,200,120);
ctx.stroke();
ctx.clip();
// 在 clip() 之后繪制綠色矩形
ctx.fillStyle="green";
ctx.fillRect(0,0,150,100);
不使用clip
使用clip
- 15.2.4 繪制文本
- 繪制文本主要有兩個方法: fillText()和 strokeText()。這兩個方法都可以接收 4 個參數:要繪制的文本字符串、 x 坐標、 y 坐標和可選的最大像素寬度。
- 第四個參數表示字符的最大寬度限制:傳入的字符串大于最大寬度,則繪制的文本字符的高度正確,但寬度會收縮以適應最大寬度。
- font屬性:表示文本樣式、大小及字體,用 CSS 中指定字體的格式來指定,例如"10px Arial"。
- textAlign屬性:表示文本對齊方式。可能的有"start"、"end"、"left"、"right"和"center"。建議使用"start"和"end",不要使用"left"和"right",因為前兩者的意思更穩妥,能同時適合從左到右和從右到左顯示(閱讀)的語言。
- textBaseline屬性:表示文本的基線。可能的值有"top"、"hanging"、"middle"、"alphabetic"、"ideographic"和"bottom"。
- alphabetic:默認。文本基線是普通的字母基線。
- top:文本基線是 em 方框的頂端。。
- hanging:文本基線是懸掛基線。
- middle:文本基線是 em 方框的正中。
- ideographic:文本基線是表意基線。
- bottom:文本基線是 em 方框的底端。
textBaseline
- **Context.measureText() **
- 方法返回一個 TextMetrics 對象,包含關于文本尺寸的信息(例如文本的寬度)。
var text = ctx.measureText("foo"); // TextMetrics object
text.width; // 16;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
fontsize = 150;
ctx.font = fontsize + "px Arial";
while(ctx.measureText("Hello world!").width > 500){
fontsize-- ;
ctx.font = fontsize + "px Arial";
}
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.translate(250, 250);
ctx.rotate(90 * Math.PI / 180);
ctx.scale(0.5, 0.5);
ctx.fillText("Hello World", 10 , 10);
- 15.2.5 變換
- rotate(angle):圍繞原點旋轉圖像 angle 弧度;要移動圓點, 使用下面的translate(x, y)方法;
- 怎樣實現原地旋轉:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "pink";
ctx.fillRect(100,100,100,100)
var deg = Math.PI/180; //deg = 1°
ctx.translate(150, 150); //把原點點從零移到正方形的中心點
ctx.rotate(45*deg); //繞著原點旋轉45度(即繞著圖像中心點旋轉45度), 下面的transform也是同樣作用
// transfrom要實現旋轉,需要設置前4個參數
// ctx.transform(Math.cos(45*deg),Math.sin(45*deg),-Math.sin(45*deg),Math.cos(45*deg),0,0);
ctx.fillStyle = "blue";
ctx.fillRect(-50,-50,100,100) //旋轉后的圖像坐標需要從新的圓點回到原來的坐標, 即-width, -height
- translate(x, y):將坐標原點移動到(x,y)。執行這個變換之后, 坐標(0,0)會變成之前由(x,y)表示的點。
ctx.font = "150px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.translate(250, 250);
ctx.rotate(90 * Math.PI / 180);
ctx.fillText("Hello World", 10 , 10);
- scale(scaleX, scaleY):縮放圖像,在 x 方向乘以 scaleX,在 y 方向乘以 scaleY。 scaleX和 scaleY 的默認值都是 1.0。
ctx.scale(0.5, 0.5);
ctx.fillText("Hello World", 10 , 10);
- transform(a, b, c, d, e, f):直接修改變換矩陣。
- 默認參數:transfrom(1, 0, 0, 1, 0, 0)
a 水平縮放繪圖
b 水平傾斜繪圖
c 垂直傾斜繪圖
d 垂直縮放繪圖
e 水平移動繪圖
f 垂直移動繪圖
- 默認參數:transfrom(1, 0, 0, 1, 0, 0)
15.2.6 繪制圖像
context.drawImage()
-
要加入事件才能在畫布里顯示出圖片
- <img onload="somefunc(event)" src="image.jpg" id="myImg">
-
如果你想把一幅圖像繪制到畫布上,可以使用 drawImage()方法。根據期望的最終結果不同,調用這個方法時,可以使用三種不同的參數組合。
- 傳入一個 HTML <img>Element,以及繪制該圖像的起點的 x 和 y 坐標.
- context.drawImage(image, 10, 10);
- 如果你想改變繪制后圖像的大小,可以再多傳入兩個參數,分別表示目標寬度和目標高度。
- context.drawImage(image, 50, 10, 20, 30);
- 還可以選擇把圖像中的某個區域繪制到上下文中。 drawImage()方法的這種調
用方式總共需要傳入 9 個參數:
要繪制的圖像、
源圖像的 x 坐標、源圖像的 y 坐標、
源圖像的寬度、源圖像的高度、
目標圖像的 x 坐標、目標圖像的 y 坐標、
目標圖像的寬度、目標圖像的高度。
- context.drawImage(圖片obj, 源圖片x,源圖片y, x開始切的寬度,x開始切的高度,畫布x點,畫布y點,畫布圖片的寬度,畫布圖片的高度)
除了給 drawImage()方法傳入 HTML <img>元素外,還可以傳入另一個<canvas>元素作為其第一個參數。這樣,就可以把另一個畫布內容繪制到當前畫布上。
獲得圖片修改后的結果:re = canvas.toDataURL(),可以把結果再放回到img元素里;img.src = re;
即圖像不能來自其他域。如果圖像來自其他域,調用toDataURL()會拋出一個錯誤。
15.2.7 陰影
2D 上下文會根據以下幾個屬性的值,自動為形狀或路徑繪制出陰影。
context.shadowColor = "rgba(0,0,255,0.1)"; // 陰影顏色,不設置默認黑色和透明度 不透明是1
context.shadowOffsetX = -100; // x 軸方向的陰影偏移量 默認0
context.shadowOffsetY = 1; // y 軸方向的陰影偏移量 默認0
context.shadowBlur = 20; // 模糊的像素數,默認 0,即不模糊
context.drawImage(img,50,50,200,150)
-
15.2.8 漸變
- 可以調用 createLinearGradient()方法。這個方法接收 4 個參數:起點的 x 坐標、起點的 y 坐標、終點的 x 坐標、終點的 y 坐標。
- 下一步就是使用 addColorStop()方法來指定色標。這個方法接收兩個參數:色標位置和 CSS 顏色值。色標位置是一個 0(開始的顏色)到 1(結束的顏色)之間的數字。
- 然后就可以把 fillStyle 或 strokeStyle 設置為這個對象,從而使用漸變來繪制形狀或描邊.
var gradient = context.createLinearGradient(30, 30, 80, 80);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//繪制漸變矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
- 放射漸變
- var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
- 前三個參數指定的是起點圓的原心(x 和 y)及半徑,后三個參數指定的是終點圓的原心(x 和 y)及半徑。可以把徑向漸變想象成一個長圓桶,而這 6 個參數定義的正是這個桶的兩個圓形開口的位置。如果把一個圓形開口定義得比另一個小一些,那這個圓桶就變成了圓錐體,而通過移動每個圓形開口的位置,就可達到像旋轉這個圓錐體一樣的效果。
var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//繪制漸變矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
- 15.2.9 模式
- 模 式 其 實 就 是 重 復 的 圖 像 , 可 以 用 來 填 充 或 描 邊 圖 形 。
- 要 創 建 一 個 新 模 式 , 可 以 調 用createPattern()方法。
- 有2個參數:1.imgElement 2.repeat方式: 包括"repeat"、 "repeat-x"、"repeat-y"和"no-repeat"。
img = document.getElementById("myImg");
canvasB = document.getElementById("myCanvasB");
contextB = canvasB.getContext("2d");
// 獲取圖像,把圖像縮小到100x100像素;
contextB.drawImage(img, 0,0,100,100);
canvasA = document.getElementById("myCanvasA");
contextA = canvasA.getContext("2d");
// convasA的Context里創建Pattern,放縮小了的canvasB畫布和repeat模式
pattern = contextA.createPattern(canvasB,"repeat");
// 填充規則
contextA.fillStyle = pattern;
contextA.fillRect(0,0,500,500)
}
- createPattern()方法的第一個參數也可以是一個<video>元素,或者另一個<canvas>元素。
- 15.2.10 使用圖像數據
img = document.getElementById("myImg");
var c=document.getElementById("drawing");
var ctx=c.getContext("2d");
// img放到畫布里;
ctx.drawImage(img,0,0,500,500);
// 取得圖像數據
imgData = ctx.getImageData(0,0,c.width,c.height);
// 每個 ImageData 對象都有三個屬性: width、 height 和data。
// 在imgData.data數組中,存著每一個像素點的rgba的值;
data = imgData.data;
for (let i=0, len=data.length; i<len; i+=4){
//得到每個像素點的數據
red = data[i];
green = data[i+1];
blue = data[i+2];
//求得每個像素點rgb的平均值
var average = Math.floor((red+green+blue)/3);
//設置顏色值,透明度不變
data[i] = average;
data[i+1] = average;
data[i+2] = average;
}
// 覆蓋原來的imgData.data的值
imgData.data = data;
ctx.putImageData(imgData,0,0);
- 15.2.11 合成
- 上下文的屬性:context.globalAlpha 設置透明度 1不透明 0透明 ;
canvas = document.getElementById("drawing");
context = canvas.getContext("2d");
context.globalAlpha = 0.5; // 設置全局上下文的透明度
context.fillStyle = "red";
context.fillRect(0,0, 300, 150); // 半透明紅色
context.fillStyle = "blue";
context.fillRect(100,100, 300, 150); // 半透明藍色
-
globalCompositionOperation屬性 表示后繪制的圖形怎樣與先繪制的圖形結合。這個屬性的值是字符串,可能的值如下。
- source-over(默認值):后繪制的圖形位于先繪制的圖形上方。
- source-in:后繪制的圖形與先繪制的圖形重疊的部分可見,兩者其他部分完全透明。
- source-out:后繪制的圖形與先繪制的圖形不重疊的部分可見,先繪制的圖形完全透明。
- source-atop:后繪制的圖形與先繪制的圖形重疊的部分可見,先繪制圖形不受影響。
- destination-over: 后繪制的圖形位于先繪制的圖形下方,只有之前透明像素下的部分才可見。
- destination-in:后繪制的圖形位于先繪制的圖形下方,兩者不重疊的部分完全透明。
- destination-out:后繪制的圖形擦除與先繪制的圖形重疊的部分。
- destination-atop:后繪制的圖形位于先繪制的圖形下方,在兩者不重疊的地方,先繪制的圖形會變透明。
- lighter:后繪制的圖形與先繪制的圖形重疊部分的值相加,使該部分變亮。
- copy:后繪制的圖形完全替代與之重疊的先繪制圖形。
- xor:后繪制的圖形與先繪制的圖形重疊的部分執行“異或”操作。