前面我整理過一篇文章canvas學習之API整理筆記(一),從這篇文章咱們已經能夠基本瞭解到經常使用繪圖的API、簡單的變換和動畫。而本篇文章的主要內容包括高級動畫、像素操做、性能優化等知識點,講解每一個知識點的同時還會有一些酷炫的demo,保證看官們全程在線,毫無尿點,看完不會後悔,哈哈,一個耿直的笑^_^。
除此以外,關於canvas的一系列實例即未來襲!歡迎關注!css
var can = document.getElementById(‘canvas’); //建立一個畫布 var ctx = can.getContext(‘2d’);
下面全部的操做都在畫布ctx上進行操做。html
繼上一篇簡單介紹了動畫(主要是requestAnimationFrame方法),如今咱們來一步步實現一個在畫布內滾動的實例。canvas
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 = { //小球屬性,原點位置,速度,半徑等。 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); //循環執行 } draw();
上面代碼實現的效果以下圖:性能優化
代碼我已經寫了基本的註釋,不難理解,簡單歸納一下這個實例的實現思想
:dom
建立一個小球對象,包含一個繪製本身的方法。在整個畫布中繪製這個小球,而後在下一次繪製以前,先清除整個畫布,改變小球的各個屬性(包含了邏輯,好比邊界的判斷),而後從新繪製一遍,從而達到了動起來的效果。函數
若是你把上面代碼中的ctx.clearRect(0,0, canvas.width, canvas.height);
換成了下面這樣:性能
ctx.fillStyle = 'rgba(255,255,255,0.3)'; ctx.fillRect(0, 0, canvas.width, canvas.height);
就能夠獲得漸變尾巴的效果:
若是咱們想對一個canvas畫布進行以下操做:獲取每個點的信息,對每個座標點進行操做。那咱們就須要瞭解一下ImageData對象
了。
ImageData對象(由getImageData方法獲取的)中存儲着canvas對象真實的像素數據,它包含如下幾個只讀屬性:
width
圖片寬度,單位是像素。
height
圖片高度,單位是像素。
data
Uint8ClampedArray類型的一維數組,包含着RGBA格式的整型數據,範圍在0至255之間(包括255)。簡單講,就是一個數組,每四個元素存儲一個點的顏色信息,這四個元素分別對應爲R、G、B、A的值
(知道顏色取值的一眼就明白了,不知道的也不要緊,後面有實例,一看就明白)。
去建立一個新的,空白的ImageData對像,你應該會使用createImageData()方法
:
var myImageData = ctx.createImageData(width, height);
上面代碼建立了一個新的具體特定尺寸的ImageData對像。全部像素被預設爲透明黑。
爲了得到一個包含畫布場景像素數據的ImageData對像,你能夠用getImageData()方法
:
var myImageData = ctx.getImageData(left, top, width, height);
建立的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); //建立ImageData對象 var data = area.data; //獲取data屬性(一個存儲顏色rgba值的數組) var rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + data[3] + ')'; color.style.color = rgba; color.textContent = rgba; } can.addEventListener('mousemove', pick);
實現的效果以下圖:
注意:
若是有些同窗試到這裏發現有這個報錯內容Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
,須要檢查這行代碼:
img.src = "img_the_scream.jpg";
這裏的圖片地址不能是跨域地址
。網上有一些解決辦法,這裏就不展開講了。
你能夠用putImageData()方法
去對場景進行像素數據的寫入。
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); //獲取ImageData var 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(); //反轉色 toGray(); //變灰色
實現的效果以下圖:
瀏覽器爲了達到抗鋸齒的效果會作額外的運算。爲了不這種狀況,請保證使用canvas的繪製函數時,儘可能用Math.floor()
函數對全部的座標點取整。好比:
ctx.drawImage(myImage, 0.3, 0.5); //不提倡這樣寫,應該像下面這樣處理 ctx.drawImage(myImage, Math.floor(0.3), Math.floor(0.5));
好比作一個遊戲,有幾個層面:背景層(簡單變化)、遊戲層(時刻變化)
。這個時候,咱們就能夠建立兩個畫布,一個專門用來繪製不變的背景(少許繪製),另外一個用來繪製遊戲動態部分(大量繪製),就像這樣:
<canvas id="background-can" width="480" height="320"></canvas> <canvas id="game-can" width="480" height="320"></canvas>
若是有一層是永遠不變的,好比一張靜態的背景圖,最好使用div+css的方法去替代ctx.drawimage()
,這麼作能夠避免在每一幀在畫布上繪製大圖。簡單講,dom渲染確定比canvas的操做性能更高。
若是要對一個畫布進行縮放,若是能夠的話,儘可能使用CSS3的transform來實現。總之,記住一個原則,能用html+div實現的儘可能不用js對canvas進行操做。
OK,canvas經常使用的API就基本總結完了,靠這些API已經足夠開發一些中型遊戲了。好比以前本身寫的實例demo之小遊戲tinyHeart,就是用這些函數畫出來的。關鍵是這些函數的組合使用,多多聯繫就行了。
若是你把我以前的兩篇文章都看了的話,相信你會對canvas愈來愈感興趣。因此爲了作一個善始善終的人,我後續還會出一系列的關於canvas的實例,注意,是一系列!敬請期待!