前面我整理過一篇文章介紹了一些基本的API,從這篇文章我們已經(jīng)可以基本了解到常用繪圖的API、簡單的變換和動畫。而本篇文章的主要內(nèi)容包括高級動畫、像素操作、性能優(yōu)化等知識點,講解每個知識點的同時還會有一些酷炫的demo,保證看官們?nèi)淘诰€,毫無尿點,看完不會后悔,哈哈,一個耿直的笑^_^。
除此之外,關(guān)于canvas的一系列實例即將來襲!歡迎關(guān)注!
開始之前
//獲取canvas容器var can = document.getElementById('canvas');//創(chuàng)建一個畫布var ctx = can.getContext('2d');下面所有的操作都在畫布ctx上進行操作。
高級動畫
繼上一篇簡單介紹了動畫(主要是requestAnimationFrame方法),現(xiàn)在我們來一步步實現(xiàn)一個在畫布內(nèi)滾動的實例。
html代碼:
<canvas id="canvas" width="400" height="200" style="background:#fff;"></canvas>
js代碼:
var canvas = document.getElementById('canvas');var ctx = canvas.getContext('2d');var ball = { //小球?qū)傩裕c位置,速度,半徑等。 x: 100, y: 100, vx: 4, vy: 2, radius: 20, color: 'blue', draw: function() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, true); ctx.closePath(); ctx.fillStyle = this.color; ctx.fill(); }};function draw() { ctx.clearRect(0,0, canvas.width, canvas.height); //繪制之前清除整個畫布 ball.draw(); //在畫布中繪制小球 ball.x += ball.vx; //改變小球位置坐標 ball.y += ball.vy; //改變小球位置坐標 if (ball.y + ball.vy > canvas.height-15 || ball.y + ball.vy < 15) { //邊界判斷 ball.vy = -ball.vy; } if (ball.x + ball.vx > canvas.width-15 || ball.x + ball.vx < 15) { //邊界判斷 ball.vx = -ball.vx; } window.requestAnimationFrame(draw); //循環(huán)執(zhí)行}draw();上面代碼實現(xiàn)的效果如下圖:

代碼我已經(jīng)寫了基本的注釋,不難理解,簡單概括一下這個實例的實現(xiàn)思想:
創(chuàng)建一個小球?qū)ο螅粋€繪制自己的方法。在整個畫布中繪制這個小球,然后在下一次繪制之前,先清除整個畫布,改變小球的各個屬性(包含了邏輯,比如邊界的判斷),然后重新繪制一遍,從而達到了動起來的效果。
如果你把上面代碼中的
ctx.clearRect(0,0, canvas.width, canvas.height);
換成下面這樣:
ctx.fillStyle = 'rgba(255,255,255,0.3)';ctx.fillRect(0, 0, canvas.width, canvas.height);
就可以得到漸變尾巴的效果:

大概意思就是使用半透明的白色背景填充畫布來代替直接清除這個畫布,從而實現(xiàn)了想要的效果。
像素操作
如果我們想對一個canvas畫布進行如下操作:獲取每一個點的信息,對每一個坐標點進行操作。那我們就需要了解一下ImageData對象了。
ImageData對象(由getImageData方法獲取的)中存儲著canvas對象真實的像素數(shù)據(jù),它包含以下幾個只讀屬性:
width
圖片寬度,單位是像素。
height
圖片高度,單位是像素。
data
Uint8ClampedArray類型的一維數(shù)組,包含著RGBA格式的整型數(shù)據(jù),范圍在0至255之間(包括255)。簡單講,就是一個數(shù)組,每四個元素存儲一個點的顏色信息,這四個元素分別對應(yīng)為R、G、B、A的值(知道顏色取值的一眼就明白了,不知道的也沒關(guān)系,后面有實例,一看就明白)。
創(chuàng)建ImageData對象
去創(chuàng)建一個新的,空白的ImageData對像,你應(yīng)該會使用createImageData()方法:
var myImageData = ctx.createImageData(width, height);
上面代碼創(chuàng)建了一個新的具體特定尺寸的ImageData對像。所有像素被預(yù)設(shè)為透明黑。
獲取像素數(shù)據(jù)
為了獲得一個包含畫布場景像素數(shù)據(jù)的ImageData對像,你可以用getImageData()方法:
var myImageData = ctx.getImageData(left, top, width, height);
創(chuàng)建的myImageData對象就有width、height、data三個屬性的值了。看下面這個實例:
html代碼:
<div id="color">hover處的顏色</div><canvas id="myCanvas" width="300" height="150"></canvas>
js代碼:
var can = document.getElementById('myCanvas');var ctx = can.getContext('2d');var img = new Image(); img.src = "img_the_scream.jpg";ctx.drawImage(img, 0, 0);var color = document.getElementById('color');function pick(event) { var x = event.layerX; var y = event.layerY; var area = ctx.getImageData(x, y, 1, 1); //創(chuàng)建ImageData對象 var data = area.data; //獲取data屬性(一個存儲顏色rgba值的數(shù)組) var rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + data[3] + ')'; color.style.color = rgba; color.textContent = rgba;}can.addEventListener('mousemove', pick);實現(xiàn)的效果如下圖:

注意: 如果有些同學試到這里發(fā)現(xiàn)有這個報錯內(nèi)容Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.,需要檢查這行代碼:
img.src = "img_the_scream.jpg";
這里的圖片地址不能是跨域地址。網(wǎng)上有一些解決辦法,這里就不展開講了。
寫入像素數(shù)據(jù)
你可以用putImageData()方法去對場景進行像素數(shù)據(jù)的寫入。
ctx.putImageData(myImageData, x, y); //在畫布的(x, y)點開始繪制myImageData所存儲的像素信息。
所以我們可以把獲取到的像素信息進行處理,然后再重新繪制,就得到了新的圖形。看看下面這個實例:
html代碼:
<canvas id="canvas" width="660" height="277"></canvas>
var canvas = document.getElementById('canvas');var ctx = canvas.getContext('2d');var img = new Image();img.src = 'img_the_scream.jpg';ctx.drawImage(img, 0, 0);var imageData = ctx.getImageData(0,0,canvas.width, canvas.height); //獲取ImageDatavar colors = imageData.data; //獲取像素信息function invert() { for (var i = 0; i < colors.length; i += 4) { //四個為一組 colors[i] = 225 - colors[i]; // red colors[i+1] = 225 - colors[i+1]; // green colors[i+2] = 225 - colors[i+2]; // blue colors[i+3] = 255; //alpha } ctx.putImageData(imageData, 220, 0); //從(220, 0)開始繪制改變過的顏色}function toGray() { for (var i = 0; i < colors.length; i += 4) { var avg = (colors[i] + colors[i+1] + colors[i+2]) / 3; colors[i] = avg; // red colors[i+1] = avg; // green colors[i+2] = avg; // blue colors[i+3] = 255; //alpha } ctx.putImageData(imageData, 440, 0); //從(440, 0)開始繪制改變過的顏色}invert(); //反轉(zhuǎn)色toGray(); //變灰色實現(xiàn)的效果如下圖:

左邊部分是原圖,中間部分是把原圖顏色經(jīng)過反轉(zhuǎn)得到的圖案,右邊部分是把原圖顏色變灰得到的圖案。
性能優(yōu)化
坐標點盡量用整數(shù)
瀏覽器為了達到抗鋸齒的效果會做額外的運算。為了避免這種情況,請保證使用canvas的繪制函數(shù)時,盡量用Math.floor()函數(shù)對所有的坐標點取整。比如:
ctx.drawImage(myImage, 0.3, 0.5); //不提倡這樣寫,應(yīng)該像下面這樣處理ctx.drawImage(myImage, Math.floor(0.3), Math.floor(0.5));
使用多個畫布繪制復雜場景
比如做一個游戲,有幾個層面:背景層(簡單變化)、游戲?qū)樱〞r刻變化)。這個時候,我們就可以創(chuàng)建兩個畫布,一個專門用來繪制不變的背景(少量繪制),另一個用來繪制游戲動態(tài)部分(大量繪制),就像這樣:
<canvas id="background-can" width="480" height="320"></canvas><canvas id="game-can" width="480" height="320"></canvas>
用CSS設(shè)置靜態(tài)大圖
如果有一層是永遠不變的,比如一張靜態(tài)的背景圖,最好使用div+css的方法去替代ctx.drawimage(),這么做可以避免在每一幀在畫布上繪制大圖。簡單講,dom渲染肯定比canvas的操作性能更高。
盡量少操作canvas的縮放
如果要對一個畫布進行縮放,如果可以的話,盡量使用CSS3的transform來實現(xiàn)。總之,記住一個原則,能用html+div實現(xiàn)的盡量不用js對canvas進行操作。
更多Tips
結(jié)語
OK,canvas常用的API就基本總結(jié)完了,靠這些API已經(jīng)足夠開發(fā)一些中型游戲了。比如之前自己寫的實例demo之小游戲tinyHeart,就是用這些函數(shù)畫出來的。關(guān)鍵是這些函數(shù)的組合使用,多多練習就好了。
如果你把我之前的兩篇文章都看了的話,相信你會對canvas越來越感興趣。所以為了讓大家的興趣不會中斷,我后續(xù)還會出一系列的關(guān)于canvas的實例,注意,是一系列!敬請期待!
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持武林網(wǎng)!
新聞熱點
疑難解答