?
本文檔使用 php中文網(wǎng)手冊(cè) 發(fā)布
概述
繪圖方法
圖像處理方法
drawImage方法
getImageData方法,putImageData方法
toDataURL方法
save方法,restore方法
動(dòng)畫
像素處理
灰度效果
復(fù)古效果
紅色蒙版效果
亮度效果
反轉(zhuǎn)效果
參考鏈接
Canvas API(畫布)用于在網(wǎng)頁實(shí)時(shí)生成圖像,并且可以操作圖像內(nèi)容,基本上它是一個(gè)可以用JavaScript操作的位圖(bitmap)。
使用前,首先需要新建一個(gè)canvas網(wǎng)頁元素。
<canvas id="myCanvas" width="400" height="200"> 您的瀏覽器不支持canvas! </canvas>
上面代碼中,如果瀏覽器不支持這個(gè)API,則就會(huì)顯示canvas標(biāo)簽中間的文字——“您的瀏覽器不支持canvas!”。
每個(gè)canvas元素都有一個(gè)對(duì)應(yīng)的context對(duì)象(上下文對(duì)象),Canvas API定義在這個(gè)context對(duì)象上面,所以需要獲取這個(gè)對(duì)象,方法是使用getContext方法。
var canvas = document.getElementById('myCanvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); }
上面代碼中,getContext方法指定參數(shù)2d,表示該canvas對(duì)象用于生成2D圖案(即平面圖案)。如果參數(shù)是3d,就表示用于生成3D圖像(即立體圖案),這部分實(shí)際上單獨(dú)叫做WebGL API(本書不涉及)。
canvas畫布提供了一個(gè)用來作圖的平面空間,該空間的每個(gè)點(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)行線的著色,這時(shí)整條線才變得可見
moveto和lineto方法可以多次使用。最后,還可以使用closePath方法,自動(dòng)繪制一條當(dāng)前點(diǎn)到起點(diǎn)的直線,形成一個(gè)封閉圖形,省卻使用一次lineto方法。
(2)繪制矩形
fillRect(x, y, width, height)方法用來繪制矩形,它的四個(gè)參數(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方法用來清除某個(gè)矩形區(qū)域的內(nèi)容。
ctx.clearRect(100,50,50,50);
(3)繪制文本
fillText(string, x, y) 用來繪制文本,它的三個(gè)參數(shù)分別為文本內(nèi)容、起點(diǎn)的x坐標(biāo)、y坐標(biāo)。使用之前,需用font設(shè)置字體、大小、樣式(寫法類似與CSS的font屬性)。與此類似的還有strokeText方法,用來添加空心字。
// 設(shè)置字體 ctx.font = "Bold 20px Arial"; // 設(shè)置對(duì)齊方式 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表示做圖時(shí)應(yīng)該逆時(shí)針畫(true)還是順時(shí)針畫(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è)置對(duì)應(yīng)的圖像對(duì)象,以及它在畫布上的位置
上面代碼將一個(gè)PNG圖像載入canvas。
由于圖像的載入需要時(shí)間,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()方法接受三個(gè)參數(shù),第一個(gè)參數(shù)是圖像文件的DOM元素(即img標(biāo)簽),第二個(gè)和第三個(gè)參數(shù)是圖像左上角在Canvas元素中的坐標(biāo),上例中的(0, 0)就表示將圖像左上角放置在Canvas元素的左上角。
getImageData方法可以用來讀取Canvas的內(nèi)容,返回一個(gè)對(duì)象,包含了每個(gè)像素的信息。
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
imageData對(duì)象有一個(gè)data屬性,它的值是一個(gè)一維數(shù)組。該數(shù)組的值,依次是每個(gè)像素的紅、綠、藍(lán)、alpha通道值,因此該數(shù)組的長(zhǎng)度等于 圖像的像素寬度 x 圖像的像素高度 x 4,每個(gè)值的范圍是0–255。這個(gè)數(shù)組不僅可讀,而且可寫,因此通過操作這個(gè)數(shù)組的值,就可以達(dá)到操作圖像的目的。修改這個(gè)數(shù)組以后,使用putImageData方法將數(shù)組內(nèi)容重新繪制在Canvas上。
context.putImageData(imageData, 0, 0);
對(duì)圖像數(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è)置,然后繪制了一個(gè)有陰影的矩形。接著,使用restore方法,恢復(fù)了保存前的設(shè)置,繪制了一個(gè)沒有陰影的矩形。
利用JavaScript,可以在canvas元素上很容易地產(chǎn)生動(dòng)畫效果。
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);
上面代碼會(huì)產(chǎn)生一個(gè)小圓點(diǎn),每隔30毫秒就向右下方移動(dòng)的效果。setInterval函數(shù)的一開始,之所以要將畫布重新渲染黑色底色,是為了抹去上一步的小圓點(diǎn)。
通過設(shè)置圓心坐標(biāo),可以產(chǎn)生各種運(yùn)動(dòng)軌跡。
先上升后下降。
var vx = 10, vy = -10, gravity = 1; setInterval(function() { posX += vx; posY += vy; vy += gravity; // ... });
上面代碼中,x坐標(biāo)始終增大,表示持續(xù)向右運(yùn)動(dòng)。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軸移動(dòng)的速度變?yōu)樵瓉淼?5%,而向y軸反彈上一次反彈高度的40%。
通過getImageData方法和putImageData方法,可以處理每個(gè)像素,進(jìn)而操作圖像內(nèi)容。
假定filter是一個(gè)處理像素的函數(shù),那么整個(gè)對(duì)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)三個(gè)像素值的算術(shù)平均值,這實(shí)際上將圖像轉(zhuǎn)成了黑白形式。假定d[i]是像素?cái)?shù)組中一個(gè)象素的紅色值,則d[i+1]為綠色值,d[i+2]為藍(lán)色值,d[i+3]就是alpha通道值。轉(zhuǎn)成灰度的算法,就是將紅、綠、藍(lán)三個(gè)值相加后除以3,再將結(jié)果寫回?cái)?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)三個(gè)像素,分別取這三個(gè)值的某種加權(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)三個(gè)值的平均值,而將綠色通道和藍(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)色通道,同時(shí)加上一個(gè)正值或負(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