?
This document uses PHP Chinese website manual Release
概述
繪圖方法
圖像處理方法
drawImage方法
getImageData方法,putImageData方法
toDataURL方法
save方法,restore方法
動畫
像素處理
灰度效果
復(fù)古效果
紅色蒙版效果
亮度效果
反轉(zhuǎn)效果
參考鏈接
Canvas API(畫布)用于在網(wǎng)頁實(shí)時生成圖像,并且可以操作圖像內(nèi)容,基本上它是一個可以用JavaScript操作的位圖(bitmap)。
使用前,首先需要新建一個canvas網(wǎng)頁元素。
<canvas id="myCanvas" width="400" height="200"> 您的瀏覽器不支持canvas! </canvas>
上面代碼中,如果瀏覽器不支持這個API,則就會顯示canvas標(biāo)簽中間的文字——“您的瀏覽器不支持canvas!”。
每個canvas元素都有一個對應(yīng)的context對象(上下文對象),Canvas API定義在這個context對象上面,所以需要獲取這個對象,方法是使用getContext方法。
var canvas = document.getElementById('myCanvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); }
上面代碼中,getContext方法指定參數(shù)2d,表示該canvas對象用于生成2D圖案(即平面圖案)。如果參數(shù)是3d,就表示用于生成3D圖像(即立體圖案),這部分實(shí)際上單獨(dú)叫做WebGL API(本書不涉及)。
canvas畫布提供了一個用來作圖的平面空間,該空間的每個點(diǎn)都有自己的坐標(biāo),x表示橫坐標(biāo),y表示豎坐標(biāo)。原點(diǎn)(0, 0)位于圖像左上角,x軸的正向是原點(diǎn)向右,y軸的正向是原點(diǎn)向下。
(1)繪制路徑
beginPath方法表示開始繪制路徑,moveTo(x, y)方法設(shè)置線段的起點(diǎn),lineTo(x, y)方法設(shè)置線段的終點(diǎn),stroke方法用來給透明的線段著色。
ctx.beginPath(); // 開始路徑繪制 ctx.moveTo(20, 20); // 設(shè)置路徑起點(diǎn),坐標(biāo)為(20,20) ctx.lineTo(200, 20); // 繪制一條到(200,20)的直線 ctx.lineWidth = 1.0; // 設(shè)置線寬 ctx.strokeStyle = "#CC0000"; // 設(shè)置線的顏色 ctx.stroke(); // 進(jìn)行線的著色,這時整條線才變得可見
moveto和lineto方法可以多次使用。最后,還可以使用closePath方法,自動繪制一條當(dāng)前點(diǎn)到起點(diǎn)的直線,形成一個封閉圖形,省卻使用一次lineto方法。
(2)繪制矩形
fillRect(x, y, width, height)方法用來繪制矩形,它的四個參數(shù)分別為矩形左上角頂點(diǎn)的x坐標(biāo)、y坐標(biāo),以及矩形的寬和高。fillStyle屬性用來設(shè)置矩形的填充色。
ctx.fillStyle = 'yellow'; ctx.fillRect(50, 50, 200, 100);
strokeRect方法與fillRect類似,用來繪制空心矩形。
ctx.strokeRect(10,10,200,100);
clearRect方法用來清除某個矩形區(qū)域的內(nèi)容。
ctx.clearRect(100,50,50,50);
(3)繪制文本
fillText(string, x, y) 用來繪制文本,它的三個參數(shù)分別為文本內(nèi)容、起點(diǎn)的x坐標(biāo)、y坐標(biāo)。使用之前,需用font設(shè)置字體、大小、樣式(寫法類似與CSS的font屬性)。與此類似的還有strokeText方法,用來添加空心字。
// 設(shè)置字體 ctx.font = "Bold 20px Arial"; // 設(shè)置對齊方式 ctx.textAlign = "left"; // 設(shè)置填充顏色 ctx.fillStyle = "#008600"; // 設(shè)置字體內(nèi)容,以及在畫布上的位置 ctx.fillText("Hello!", 10, 50); // 繪制空心字 ctx.strokeText("Hello!", 10, 100);
fillText方法不支持文本斷行,即所有文本出現(xiàn)在一行內(nèi)。所以,如果要生成多行文本,只有調(diào)用多次fillText方法。
(4)繪制圓形和扇形
arc方法用來繪制扇形。
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
arc方法的x和y參數(shù)是圓心坐標(biāo),radius是半徑,startAngle和endAngle則是扇形的起始角度和終止角度(以弧度表示),anticlockwise表示做圖時應(yīng)該逆時針畫(true)還是順時針畫(false)。
下面是如何繪制實(shí)心的圓形。
ctx.beginPath(); ctx.arc(60, 60, 50, 0, Math.PI*2, true); ctx.fillStyle = "#000000"; ctx.fill();
繪制空心圓形的例子。
ctx.beginPath(); ctx.arc(60, 60, 50, 0, Math.PI*2, true); ctx.lineWidth = 1.0; ctx.strokeStyle = "#000"; ctx.stroke();
(5)設(shè)置漸變色
createLinearGradient方法用來設(shè)置漸變色。
var myGradient = ctx.createLinearGradient(0, 0, 0, 160); myGradient.addColorStop(0, "#BABABA"); myGradient.addColorStop(1, "#636363");
createLinearGradient方法的參數(shù)是(x1, y1, x2, y2),其中x1和y1是起點(diǎn)坐標(biāo),x2和y2是終點(diǎn)坐標(biāo)。通過不同的坐標(biāo)值,可以生成從上至下、從左到右的漸變等等。
使用方法如下:
ctx.fillStyle = myGradient; ctx.fillRect(10,10,200,100);
(6)設(shè)置陰影
一系列與陰影相關(guān)的方法,可以用來設(shè)置陰影。
ctx.shadowOffsetX = 10; // 設(shè)置水平位移 ctx.shadowOffsetY = 10; // 設(shè)置垂直位移 ctx.shadowBlur = 5; // 設(shè)置模糊度 ctx.shadowColor = "rgba(0,0,0,0.5)"; // 設(shè)置陰影顏色 ctx.fillStyle = "#CC0000"; ctx.fillRect(10,10,200,100);
canvas允許將圖像文件插入畫布,做法是讀取圖片后,使用drawImage方法在畫布內(nèi)進(jìn)行重繪。
var img = new Image(); img.src = "image.png"; ctx.drawImage(img, 0, 0); // 設(shè)置對應(yīng)的圖像對象,以及它在畫布上的位置
上面代碼將一個PNG圖像載入canvas。
由于圖像的載入需要時間,drawImage方法只能在圖像完全載入后才能調(diào)用,因此上面的代碼需要改寫。
var image = new Image(); image.onload = function() { var canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; canvas.getContext("2d").drawImage(image, 0, 0); return canvas; } image.src = "image.png";
drawImage()方法接受三個參數(shù),第一個參數(shù)是圖像文件的DOM元素(即img標(biāo)簽),第二個和第三個參數(shù)是圖像左上角在Canvas元素中的坐標(biāo),上例中的(0, 0)就表示將圖像左上角放置在Canvas元素的左上角。
getImageData方法可以用來讀取Canvas的內(nèi)容,返回一個對象,包含了每個像素的信息。
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
imageData對象有一個data屬性,它的值是一個一維數(shù)組。該數(shù)組的值,依次是每個像素的紅、綠、藍(lán)、alpha通道值,因此該數(shù)組的長度等于 圖像的像素寬度 x 圖像的像素高度 x 4,每個值的范圍是0–255。這個數(shù)組不僅可讀,而且可寫,因此通過操作這個數(shù)組的值,就可以達(dá)到操作圖像的目的。修改這個數(shù)組以后,使用putImageData方法將數(shù)組內(nèi)容重新繪制在Canvas上。
context.putImageData(imageData, 0, 0);
對圖像數(shù)據(jù)做出修改以后,可以使用toDataURL方法,將Canvas數(shù)據(jù)重新轉(zhuǎn)化成一般的圖像文件形式。
function convertCanvasToImage(canvas) { var image = new Image(); image.src = canvas.toDataURL("image/png"); return image; }
上面的代碼將Canvas數(shù)據(jù),轉(zhuǎn)化成PNG data URI。
save方法用于保存上下文環(huán)境,restore方法用于恢復(fù)到上一次保存的上下文環(huán)境。
ctx.save(); ctx.shadowOffsetX = 10; ctx.shadowOffsetY = 10; ctx.shadowBlur = 5; ctx.shadowColor = "rgba(0,0,0,0.5)"; ctx.fillStyle = "#CC0000"; ctx.fillRect(10,10,150,100); ctx.restore(); ctx.fillStyle = "#000000"; ctx.fillRect(180,10,150,100);
上面代碼先用save方法,保存了當(dāng)前設(shè)置,然后繪制了一個有陰影的矩形。接著,使用restore方法,恢復(fù)了保存前的設(shè)置,繪制了一個沒有陰影的矩形。
利用JavaScript,可以在canvas元素上很容易地產(chǎn)生動畫效果。
var posX = 20, posY = 100; setInterval(function() { context.fillStyle = "black"; context.fillRect(0,0,canvas.width, canvas.height); posX += 1; posY += 0.25; context.beginPath(); context.fillStyle = "white"; context.arc(posX, posY, 10, 0, Math.PI*2, true); context.closePath(); context.fill(); }, 30);
上面代碼會產(chǎn)生一個小圓點(diǎn),每隔30毫秒就向右下方移動的效果。setInterval函數(shù)的一開始,之所以要將畫布重新渲染黑色底色,是為了抹去上一步的小圓點(diǎn)。
通過設(shè)置圓心坐標(biāo),可以產(chǎn)生各種運(yùn)動軌跡。
先上升后下降。
var vx = 10, vy = -10, gravity = 1; setInterval(function() { posX += vx; posY += vy; vy += gravity; // ... });
上面代碼中,x坐標(biāo)始終增大,表示持續(xù)向右運(yùn)動。y坐標(biāo)先變小,然后在重力作用下,不斷增大,表示先上升后下降。
小球不斷反彈后,逐步趨于靜止。
var vx = 10, vy = -10, gravity = 1; setInterval(function() { posX += vx; posY += vy; if (posY > canvas.height * 0.75) { vy *= -0.6; vx *= 0.75; posY = canvas.height * 0.75; } vy += gravity; // ... });
上面代碼表示,一旦小球的y坐標(biāo)處于屏幕下方75%的位置,向x軸移動的速度變?yōu)樵瓉淼?5%,而向y軸反彈上一次反彈高度的40%。
通過getImageData方法和putImageData方法,可以處理每個像素,進(jìn)而操作圖像內(nèi)容。
假定filter是一個處理像素的函數(shù),那么整個對Canvas的處理流程,可以用下面的代碼表示。
if (canvas.width > 0 && canvas.height > 0) { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); filter(imageData); context.putImageData(imageData, 0, 0); }
以下是幾種常見的處理方法。
灰度圖(grayscale)就是取紅、綠、藍(lán)三個像素值的算術(shù)平均值,這實(shí)際上將圖像轉(zhuǎn)成了黑白形式。假定d[i]是像素數(shù)組中一個象素的紅色值,則d[i+1]為綠色值,d[i+2]為藍(lán)色值,d[i+3]就是alpha通道值。轉(zhuǎn)成灰度的算法,就是將紅、綠、藍(lán)三個值相加后除以3,再將結(jié)果寫回數(shù)組。
grayscale = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { var r = d[i]; var g = d[i + 1]; var b = d[i + 2]; d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3; } return pixels; };
復(fù)古效果(sepia)則是將紅、綠、藍(lán)三個像素,分別取這三個值的某種加權(quán)平均值,使得圖像有一種古舊的效果。
sepia = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { var r = d[i]; var g = d[i + 1]; var b = d[i + 2]; d[i] = (r * 0.393)+(g * 0.769)+(b * 0.189); // red d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue } return pixels; };
紅色蒙版指的是,讓圖像呈現(xiàn)一種偏紅的效果。算法是將紅色通道設(shè)為紅、綠、藍(lán)三個值的平均值,而將綠色通道和藍(lán)色通道都設(shè)為0。
red = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { var r = d[i]; var g = d[i + 1]; var b = d[i + 2]; d[i] = (r+g+b)/3; // 紅色通道取平均值 d[i + 1] = d[i + 2] = 0; // 綠色通道和藍(lán)色通道都設(shè)為0 } return pixels; };
亮度效果(brightness)是指讓圖像變得更亮或更暗。算法將紅色通道、綠色通道、藍(lán)色通道,同時加上一個正值或負(fù)值。
brightness = function (pixels, delta) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { d[i] += delta; // red d[i + 1] += delta; // green d[i + 2] += delta; // blue } return pixels; };
反轉(zhuǎn)效果(invert)是指圖片呈現(xiàn)一種色彩顛倒的效果。算法為紅、綠、藍(lán)通道都取各自的相反值(255-原值)。
invert = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { d[i] = 255 - d[i]; d[i+1] = 255 - d[i + 1]; d[i+2] = 255 - d[i + 2]; } return pixels; };
David Walsh, JavaScript Canvas Image Conversion
Matt West, Getting Started With The Canvas API
John Robinson, How You Can Do Cool Image Effects Using HTML5 Canvas
Ivaylo Gerchev, HTML5 Canvas Tutorial: An Introduction
Donovan Hutchinson, Particles in canvas