JavaScript的Canvas繪圖
目錄
一、Canvas簡介
<canvas>元素是HTML5新增的,一個可以使用指令碼(通常為JavaScript)在其中繪製圖像的HTML元素。它可以用來製作照片集或者製作簡單的動畫,甚至可以進行實時視訊處理和渲染。
<canvas>由幾組API構成,除了具備基本繪圖能力的2D上下文,<canvas>還具備一個名為WebGL的3D上下文。
二、Canvas基本用法
使用<canvas>元素之前,必須先設定其width和height屬性,指定可以繪圖的區域大小。
出現在開始和結束標籤中的內容是後備資訊,如果瀏覽器不支援<canvas>元素,就會顯示這些資訊。
<canvas id="drawing" width="200" height="200">A drawing of something.</canvas>
要在這塊畫布上繪圖,需要使用getContext()方法傳入引數"2d"取得2D上下文物件。
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//更多程式碼
}
使用2D繪圖上下文提供的方法,可以繪製簡單的2D圖形,比如矩形、弧線和路徑。
2D上下文的座標開始於<canvas>元素的左上角,原點座標是(0,0)。
所有座標值都基於這個原點計算,x值越大表示越靠右,y值越大表示越靠下。
三、填充和描邊
2D上下文的兩種基本繪圖操作是填充和描邊。
填充就是用指定的樣式(顏色、漸變或影象)填充圖形,fillStyle屬性設定填充樣式。
描邊就是隻在圖形的邊緣畫線,strokeStyle屬性設定描邊樣式。
這兩個屬性的值可以是字串、漸變物件或模式物件,例如:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.strokeStyle = "red";
context.fillStyle = "#0000FF";
}
四、繪製矩形
與繪製矩形有關的方法包括:
- fillRect()
- strokeRect()
- clearRect()
這三個方法都能接收4個引數:矩形的x座標、矩形的y座標、矩形的寬度和矩形高度。引數單位都是畫素。
示例1:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
//繪製半透明的藍色矩形
context.fillStyle = "rgba(0, 0, 255, 0.5)";
context.fillRect(30, 30, 50, 50);
}
示例2:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色描邊矩形
context.strokeStyle = "#F00";
context.strokeRect(10, 10, 50, 50);
//繪製半透明的藍色描邊矩形
context.strokeStyle = "rgba(0, 0, 255, 0.5)";
context.strokeRect(30, 30, 50, 50);
}
示例3:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
//繪製半透明的藍色描邊矩形
context.fillStyle = "rgba(0, 0, 255, 0.5)";
context.fillRect(30, 30, 50, 50);
//在兩個矩形重疊的地方清除一個小矩形
context.clearRect(40, 40, 10, 10);
}
五、繪製路徑
繪製路徑的方法:
- beginPath() —— 表示要開始繪製新路徑。
- moveTo(x, y) —— 將繪圖遊標移動到(x, y),不畫線。
- lineTo(x, y) —— 從上一點開始繪製一條直線,到(x, y)為止。
- rect(x, y, width, height) —— 從點(x,y)開始繪製一個矩形,寬度和高度分別由width和height指定。這個方法繪製的是矩形路徑,而不是strokeRect()和fillRect()所繪製的獨立的形狀。
- arc(x, y, radius, startAngle, endAngle, counterclockwise) —— 以(x,y)為圓心繪製一條弧線,弧線半徑為radius,起始和結束角度(用弧度表示)分別為startAngle和endAngle。最後一個引數表示startAngle和endAngle是否按逆時針方向計算,值為true表示按逆時針方向計算。
- quadraticCurveTo(cx, cy, x, y) —— 繪製二次貝塞爾曲線
- bezierCurveTo(c1x, c1y, c2x, c2y, x, y) —— 繪製三次貝塞爾曲線
- closePath() —— 繪製一條連線到路徑起點的線條。
- fill() —— 使用fillStyle填充已經完成的路徑
- stroke() —— 使用strokeStyle為已經完成的路徑描邊
5.1、繪製線段
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath(); //開始繪製新路徑
context.moveTo(50, 50); //把畫筆移動到指定的座標
context.lineTo(200, 50); //繪製一條從當前位置到指定座標(200, 50)的直線
context.stroke(); //繪製
}
5.2、繪製三角形
繪製三角形邊框:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath(); //開始繪製新路徑
context.moveTo(50, 50); //把畫筆移動到指定的座標
context.lineTo(200, 50); //繪製一條從座標(50, 50)到座標(200, 50)的直線
context.lineTo(200, 200); //繪製一條從座標(200, 50)到座標(200, 200)的直線
context.closePath(); //閉合路徑,繪製一條從路徑起點到路徑終點的直線
context.stroke() //為路徑描邊
}
填充三角形:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath(); //開始繪製新路徑
context.moveTo(50, 50); //把畫筆移動到指定的座標
context.lineTo(200, 50); //繪製一條從座標(50, 50)到座標(200, 50)的直線
context.lineTo(200, 200); //繪製一條從座標(200, 50)到座標(200, 200)的直線
context.closePath(); //閉合路徑,繪製一條從路徑起點到路徑終點的直線
context.fill() //填充三角形
}
5.3、繪製圓弧
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath(); //開始繪製新路徑
context.arc(100, 100, 99, 0, Math.PI, false);
context.stroke();
}
5.4、繪製貝塞爾曲線
二次貝塞爾曲線:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath();
context.moveTo(10, 200); //起始點
var x1 = 40, y1 = 100; //控制點
var x2 = 200, y2 = 200; //結束點
//繪製貝塞爾曲線
context.quadraticCurveTo(x1, y1, x2, y2);
context.stroke();
context.beginPath();
context.rect(10, 200, 10, 10);
context.rect(x1, y1, 10, 10);
context.rect(x2, y2, 10, 10);
context.fill();
}
三次貝塞爾曲線:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath();
context.moveTo(40, 200); //起始點
var x1 = 20, y1 = 100; //控制點1
var x2 = 100, y2 = 120; //控制點2
var x3 = 200, y3 = 200; //結束點
//繪製三次貝塞爾曲線
context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
context.stroke();
context.beginPath();
context.rect(40, 200, 10, 10);
context.rect(x1, y1, 10, 10);
context.rect(x2, y2, 10, 10);
context.rect(x3, y3, 10, 10);
context.fill();
}
5.5、線條樣式
lineWidth屬性用於設定線寬
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.beginPath();
context.moveTo(10, 10);
context.lineTo(100, 10);
context.lineWidth = 10;
context.stroke();
context.beginPath();
context.moveTo(110, 10);
context.lineTo(160, 10);
context.lineWidth = 20;
context.stroke();
}
lineCap屬性用於設定線條末端樣式,可以設定為三個值:
- butt —— 線段末端為方形
- round —— 線段末端以圓形結束
- square —— 線段末端以方形結束,但是增加了一個寬度和線段相同,高度是線段厚度一半的矩形區域。
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var lineCaps = ["butt", "round", "square"]
for(var i = 0; i < 3; i++){
context.beginPath();
context.moveTo(20 + 30 * i, 30);
context.lineTo(20 + 30 * i, 100);
context.lineWidth = 20;
context.lineCap = lineCaps[i];
context.stroke();
}
context.beginPath();
context.moveTo(0, 30);
context.lineTo(300, 30);
context.moveTo(0, 100);
context.lineTo(300, 100);
context.strokeStyle = "red";
context.lineWidth = 1;
context.stroke();
}
lineJoin屬性用於設定線條與線條間接合處的樣式,可以設定為三個值:
- round —— 通過填充一個額外的,圓心在相連部分末端的扇形,繪製拐角的形狀。圓角的半徑是線段的寬度。
- bevel —— 在相連部分的末端填充一個額外的以三角形為底的區域,每個部分都有各自獨立的矩形拐角。
- miter(預設) —— 通過延沈相連部分的外邊緣,使其相交於一點,形成一個額外的菱形區域。
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var lineJoin = ['round', 'bevel', 'miter'];
context.lineWidth = 20;
for (var i = 0; i < lineJoin.length; i++){
context.lineJoin = lineJoin[i];
context.beginPath();
context.moveTo(50, 50 + i * 50);
context.lineTo(100, 100 + i * 50);
context.lineTo(150, 50 + i * 50);
context.lineTo(200, 100 + i * 50);
context.lineTo(250, 50 + i * 50);
context.stroke();
}
}
六、透明度
globalAlpha屬性用於指定所有繪製的透明度,預設值為0。
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
//修改全域性透明度
context.globalAlpha = 0.5;
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
//重置全域性透明度
context.globalAlpha = 0;
}
七、變換
7.1、translate()
translate(x, y)用於將座標原點移動到(x, y),執行這個變換後,座標(0, 0)會變成之前由(x, y)表示的點。
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//開始路徑
context.beginPath();
//繪製外圓
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
//繪製內圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
//變換原點
context.translate(100, 100);
//繪製分針
context.moveTo(0, 0);
context.lineTo(0, -85);
//繪製時針
context.moveTo(0, 0);
context.lineTo(-65, 0);
//描邊路徑
context.stroke();
}
7.2、rotate(angle)
rotate(angle)方法能夠旋轉座標軸。
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//開始路徑
context.beginPath();
//繪製外圓
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
//繪製內圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
//變換原點
context.translate(100, 100);
//旋轉錶針
context.rotate(Math.PI / 6);
//繪製分針
context.moveTo(0, 0);
context.lineTo(0, -85);
//繪製時針
context.moveTo(0, 0);
context.lineTo(-65, 0);
//描邊路徑
context.stroke();
}
7.3、scale(scaleX, scaleY)
scale(scaleX, scaleY)方法用於縮放影象,在x方向乘以scaleX,在y方向乘以scaleY。
scaleX和scaleY的預設值都是1.0
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//開始路徑
context.beginPath();
//繪製外圓
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
//繪製內圓
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
//變換原點
context.translate(100, 100);
//旋轉錶針
context.rotate(Math.PI / 6);
//繪製分針
context.moveTo(0, 0);
context.lineTo(0, -85);
//繪製時針
context.moveTo(0, 0);
context.lineTo(-65, 0);
//放大路徑
context.scale(5, 5);
//描邊路徑
context.stroke();
}
7.4、transform(a, b, c, d, e, f)
直接修改變換矩陣,方式是乘以如下矩陣:
7.5、setTransform(a, b, c, d, e, f)
將變換矩陣重置為預設狀態,然後再呼叫transform()。
八、save()和restore()
呼叫save()方法,當時所有的設定都會進入一個棧結構,得以妥善保管。
然後可以對上下文進行其他修改,等想要回到之前儲存的設定時,可以呼叫restore()方法,在儲存設定的棧結構中向前返回一級,恢復之前的狀態。
連續呼叫save()可以把更多設定儲存到棧結構中,之後再連續呼叫restore()則可以一級一級返回。
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.fillStyle = "#F00";
context.save();
context.fillStyle = "#0F0";
context.translate(100, 100);
context.save();
context.fillStyle = "#00F";
//從點(100, 100)開始繪製藍色矩形
context.fillRect(0, 0, 100, 200);
context.restore();
//從點(110, 110)開始繪製綠色矩形
context.fillRect(10, 10, 100, 200);
context.restore();
//從點(0, 0)開始繪製紅色矩形
context.fillRect(0, 0, 100, 200);
}
九、繪製文字
繪製文字主要有兩個方法:
- fillText()
- strokeText()
這兩個方法都可以接收4個引數:要繪製的文字字串、x座標、y座標和可選的最大畫素寬度。
這兩個方法都以下列3個屬性為基礎:
- font —— 表示文字樣式、大小及字型,用CSS中指定字型的格式來指定,例如"10px Arial"
- textAlign —— 表示文字對齊方式。可能的值有"start"、"end"、"left"、"right"和"center"
- textBaseline —— 表示文字的基線,可能的值有"top"、"hanging"、"middle"、"alphabetic"、"ideographic"、"bottom"
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//開始路徑
context.beginPath();
//繪製分針
context.moveTo(100, 100);
context.lineTo(100, 15);
//描邊路徑
context.stroke();
context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20);
//起點對齊
context.textAlign = "start";
context.fillText("12", 100, 40);
//終點對齊
context.textAlign = "end";
context.strokeText("12", 100, 60);
fillText()和strokeText()方法都可以接收第四個引數,也就是文字的最大畫素寬度。提供這個引數後,呼叫fillText()或strokeText()時如果傳入的字串大於最大寬度,則繪製的文字字元的高度正確,但寬度會收縮以適應最大寬度。
十、繪製圖像
2D繪圖上下文內建了對影象的支援,可以使用drawImage()方法。
drawImage()有9個引數:要素繪製的影象、源影象的x座標、源影象的y座標、源影象的寬度、源影象的高度、目標影象的x座標、目標影象的y座標、目標影象的寬度、目標影象的高度。
①繪製圖像簡單示例:
var drawing = document.getElementById("drawing");
var img = new Image(); //建立Image物件
img.src = 'https://avatar.csdn.net/6/4/D/1_qq_35732147.jpg?1539065138'; //設定圖片源
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
img.addEventListener("load", function(){
context.drawImage(img, 0, 0); //影象載入完之後才會執行這條語句
},false);
}
使用DOM0級的Image物件可以在客戶端預先載入影象,可以像使用<img>元素一樣使用Image物件,但無法將其新增到DOM樹中。
考慮到圖片是從網路載入,所以應該保證影象載入完之後再呼叫drawImage()方法繪製圖像。
②縮放圖片簡單示例:
var drawing = document.getElementById("drawing");
var img = new Image(); //建立Image物件
img.src = 'https://avatar.csdn.net/6/4/D/1_qq_35732147.jpg?1539065138'; //設定圖片源
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
img.addEventListener("load", function(){
context.drawImage(img, 0, 0, 400, 400); //影象載入完之後才會執行這條語句
},false);
}
③切片
切片示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var img = new Image();
img.src = "https://avatar.csdn.net/6/4/D/1_qq_35732147.jpg?1539071828";
img.addEventListener("load", function(){
context.drawImage(img, 50, 50, 200, 200, 0, 0, 200, 200);
}, false);
}
十一、模式
模式其實就是重複的影象,可以用來填充或描邊圖形。
要建立一個新模式,可以呼叫createPattern()方法並傳入兩個引數:
- HTML<img>元素
- 表示如何重複影象的字串,這個引數的值與CSS的background-repeat屬性值相同,包括"repeat"、"repeat-x"、"repeat-y"、"no-repeat"
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var img = new Image();
img.src = "https://avatar.csdn.net/E/9/8/3_weixin_42058532.jpg";
img.addEventListener("load", function(){
var pattern = context.createPattern(img, "repeat");
//繪製矩形
context.fillStyle = pattern;
context.fillRect(10, 10, 200, 200);
}, false);
}
十二、使用影象資料
canvas操作影象資料使用的幾個方法:
①getImageData() —— 用於取得畫布上指定矩形的畫素資料。
具體是這個方法會返回ImageData物件,ImageData物件中包含影象資料。
getImageData()方法接收4個引數:
- 要取得畫面區域左上角的x座標
- 要取得畫面區域左上角的y座標
- 要取得畫面區域的畫素寬度
- 要取得畫面區域的畫素高度
例如要取得左上角座標為(10,5)、大小為50*50畫素的區域的影象資料:
var imageData = context.getImageData(10, 5, 50, 50);
返回的物件是ImageData的例項,每個ImageData物件都有三個屬性:
- width —— 影象的寬度
- height —— 影象的高度
- data —— 儲存影象中每一個畫素的資料的陣列,每一個畫素用4個元素來儲存,分別表示紅、綠、藍和透明度值。
②putImageData() —— 把影象資料(從指定的ImageData物件)放回畫布上。
這個方法接收以下引數:
下面的程式碼通過getImageData()取得畫布上指定矩形的畫素資料,然後通過putImageData()將影象資料放回畫布:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="green";
ctx.fillRect(10,10,50,50);
var imgData=ctx.getImageData(10,10,50,50);
ctx.putImageData(imgData,10,70);
③createImageData() —— 建立新的、空白的ImageData物件
這個方法接收的引數:
以下程式碼建立100*100畫素的ImageData物件,其中每個畫素都是紅色的,然後把它放到畫布上:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var imgData=ctx.createImageData(100,100);
for (var i=0;i<imgData.data.length;i+=4)
{
imgData.data[i+0]=255;
imgData.data[i+1]=0;
imgData.data[i+2]=0;
imgData.data[i+3]=255;
}
ctx.putImageData(imgData,10,10);
效果:
示例:
建立一個簡單的灰階過濾器:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var image = new Image();
image.src = "liyangqiao.jpg";
image.addEventListener("load", function(event){
var red, green, blue, alpha, average;
//繪製原始影象
context.drawImage(image, 0, 0);
//取得影象資料
imageData = context.getImageData(0, 0, image.width, image.height);
data = imageData.data;
for(var i = 0, len = data.length; i < len; i += 4){
red = data[i];
green = data[i + 1];
blue = data[i + 2];
alpha = data[i + 3];
//求得rgb平均值
average = Math.floor((red + green + blue) / 3);
//設定顏色值,透明度不變
data[i] = average;
data[i + 1] = average;
data[i + 2] = average;
}
//回寫影象資料並顯示結果
imageData.data = data;
context.putImageData(imageData, 0, 0);
}, false);
}
注意:只有在畫布“乾淨”的情況下(即影象並非來自其他域),才可以取得影象資料。如果畫布”不乾淨“,那麼訪問影象資料時會導致JavaScript錯誤。
十三、陰影
2D上下文會根據以下幾個屬性的值,自動為形狀或路徑繪製出陰影。
- shadowColor —— 用CSS顏色格式表示的陰影顏色,預設為黑色。
- shadowOffsetX —— 形狀或路徑x軸方向的陰影偏移量,預設為0。
- shadowOffsetY —— 形狀或路徑y軸方向的陰影偏移量,預設為0。
- shadowBlur —— 模糊的畫素數,預設0,即不模糊。
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//設定陰影
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 5;
context.shadowColor = "rgba(0, 0, 0, 0.5)";
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
十四、漸變
漸變由CanvasGradient例項表示,很容易通過2D上下文來建立和修改。
要建立一個新的線性漸變物件,可以呼叫createLinearGradient()方法。
這個方法接受4個引數:起點的x座標、起點的y座標、終點的x座標、終點的y座標。
建立了漸變物件後,下一步就是使用addColorStop()方法來指定色標。
這個方法接收兩個引數:色標位置和CSS顏色值。色標位置是一個0(開始的顏色)到1(結束的顏色)之間的數字。
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var gradient = context.createLinearGradient(30, 30, 70, 70);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//繪製紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//繪製漸變矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
}
要建立徑向漸變(或放射性漸變),可以使用createRadialGradient()方法。
這個方法接收6個引數,對應著兩個圓的圓心和半徑。前三個引數指定的是起點圓的圓心和半徑,後三個引數指定的是終點圓的圓心和半徑。
示例:
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//繪製紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//繪製漸變矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
}
十五、合成
globalCompositionOperation屬性表示後繪製的圖形怎樣與先繪製的圖形結合。
這個屬性的值是字串,可能的值如下:
- source-over(預設值) —— 後繪製的圖形位於先繪製的圖形上方
- source-in —— 後繪製的圖形與先繪製的圖形重疊的部分可見,兩者其他部分完全透明
- source-out —— 後繪製的圖形與先繪製的圖形不重疊的部分可見,先繪製的圖形完全透明
- source-atop —— 後繪製的圖形與先繪製的圖形重疊部分可見,先繪製圖形不受影響
- destination-over —— 後繪製的圖形位於先繪製的圖形下方,只有之前透明畫素下的部分才可見
- destination-in —— 後繪製的圖形位於先繪製的圖形下方,兩者不重疊的部分完全透明
- destination-out —— 後繪製的圖形擦除與先繪製的圖形重疊的部分
- destination-atop —— 後繪製的圖形位於先繪製的圖形下方,在兩者不重疊的地方,先繪製的圖形會變透明
- lighter —— 後繪製的圖形與先繪製的圖形重疊部分的值相加,使該部分變亮
- copy —— 後繪製的圖形完全替代與之重疊的先繪製圖形
- xor —— 後繪製的圖形與先繪製的圖形重疊的部分執行“異或”操作
①source-over(預設值)
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
context.globalCompositeOperation = "source-over";
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
②source-in
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "source-in";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
③source-out
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "source-out";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
④source-atop
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "source-atop";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑤destination-over
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "destination-over";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑥destination-in
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "destination-in";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑦destination-out
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "destination-out";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑧destination-atop
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "destination-atop";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑨lighter
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "lighter";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑩copy
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "copy";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
⑾xor
var drawing = document.getElementById("drawing");
//確定瀏覽器支援<canvas>元素
if(drawing.getContext){
var context = drawing.getContext("2d"); //取得2D上下文物件
//繪製紅色矩形
context.fillStyle = "#F00";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "xor";
//繪製藍色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);
}
十六、基本動畫
16.1、動畫的基本步驟
①清空canvas
除非要畫的內容會完全充滿canvas(例如背景圖),否則需要清空所有。
最簡單的做法就是用clearRect()方法。
②儲存canvas狀態
如果要改變一些會改變canvas狀態的設定(樣式,變形之類的),又要在每畫一幀之時都是原始狀態的話,需要先儲存一下。
③繪製動畫圖形
這一步才是重繪動畫幀
④恢復canvas狀態
如果已經儲存了canvas的狀態,可以先恢復它,然後重繪下一幀。
16.2、操縱動畫
在canvas上繪製內容是用canvas提供的或者自定義的方法,而通常,我們僅僅在指令碼執行結束後才能看見結果,比如說,在for迴圈裡面做完成動畫是不太可能的,因為for迴圈一直高速執行,動畫都來不及繪製。
因此,為了實現動畫,我們需要一些可以定時執行重繪的方法。
首先,可以用window.setInterval(),window.setTimeout(),和window.requestAnimationFrame()來設定定期執行一個指定函式:
- setInterval(function, delay) —— 當設定好間隔時間後,function會定期執行
- setTimeout(function, delay) —— 在設定好的時間之後執行函式
- requestAnimationFrame(callback) —— 告訴瀏覽器希望執行一個動畫,並在重繪之前,請求瀏覽器執行一個特定的函式來更新動畫
如果並不需要與使用者互動,可以使用setInterval()方法,它就可以定期執行指定程式碼。
如果需要做一個遊戲,我們可以使用鍵盤或者滑鼠事件配合上setTimeout()方法來實現。
通過設定事件監聽,可以捕捉使用者的互動,並執行相應的動作。
16.3、太陽系的動畫
下面的例子,採用window.requestAnimationFrame()實現動畫效果。
這個方法提供了更加平緩並更加有效率的方式來執行動畫,當系統準備好了重繪條件的時候,才呼叫繪製動畫幀。
一般每秒鐘回撥函式執行60次,也有可能會被降低。
一個小型的太陽系模擬動畫:
var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
window.requestAnimationFrame(draw);
}
function draw(){
var ctx = document.getElementById("drawing").getContext('2d');
//後繪製的圖形位於先繪製的圖形下方,只有之前透明畫素下的部分才可見
ctx.globalCompositeOperation = 'destination-over';
ctx.clearRect(0, 0, 300, 300); //clear canvas
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)';
ctx.save();
//移動座標原點
ctx.translate(150, 150);
// Earth
var time = new Date();
ctx.rotate( ((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds() );
ctx.translate(105, 0);
ctx.fillRect(0, -12, 50, 24); //Shadow
ctx.drawImage(earth, -12, -12);
// Moon
ctx.save();
ctx.rotate( ((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds() );
ctx.translate(0, 28.5);
ctx.drawImage(moon, -3.5, -3.5);
ctx.restore();
ctx.restore();
ctx.beginPath();
ctx.arc(150, 150, 105, 0, Math.PI * 2, false); // Earth orbit
ctx.stroke();
ctx.drawImage(sun, 0, 0, 300, 300);
window.requestAnimationFrame(draw);
}
init();