canvas學習之API整理筆記(二)

前面我整理過一篇文章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對象

去建立一個新的,空白的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>

用CSS設置靜態大圖

若是有一層是永遠不變的,好比一張靜態的背景圖,最好使用div+css的方法去替代ctx.drawimage(),這麼作能夠避免在每一幀在畫布上繪製大圖。簡單講,dom渲染確定比canvas的操做性能更高。

儘可能少操做canvas的縮放

若是要對一個畫布進行縮放,若是能夠的話,儘可能使用CSS3的transform來實現。總之,記住一個原則,能用html+div實現的儘可能不用js對canvas進行操做。

更多

  • 將畫布的函數調用集合到一塊兒(例如,畫一條折線,而不要畫多條分開的直線)
  • 使用不一樣的辦法去清除畫布(clearRect()、fillRect()、調整canvas大小)
  • 儘量避免 shadowBlur特性
  • 有動畫,請使用window.requestAnimationFrame() 而非window.setInterval()

結語

OK,canvas經常使用的API就基本總結完了,靠這些API已經足夠開發一些中型遊戲了。好比以前本身寫的實例demo之小遊戲tinyHeart,就是用這些函數畫出來的。關鍵是這些函數的組合使用,多多聯繫就行了。

若是你把我以前的兩篇文章都看了的話,相信你會對canvas愈來愈感興趣。因此爲了作一個善始善終的人,我後續還會出一系列的關於canvas的實例,注意,是一系列!敬請期待!

相關文章
相關標籤/搜索