1. 程式人生 > 其它 >前端開發系列027-基礎篇之Canvas繪圖(影象)

前端開發系列027-基礎篇之Canvas繪圖(影象)

title: '前端開發系列027-基礎篇之Canvas繪圖(影象)'
tags:
  - javaScript系列
categories: []
date: 2017-07-25 08:20:13
本文將介紹Canvas畫布影象繪製相關的技術細節。

一、Canvas畫布影象繪製基礎

核心API

繪製圖像

語法

  • ctx.drawImage(image,dx,dy);
  • ctx.drawImage(image,dx,dy,dw,dh);
  • ctx.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh);

作用 該方法可以將一幅影象繪製到Canvas畫布中(源影象 - 目標影象)。

引數

dx         目標影象的原點座標(X軸)
dy         目標影象的原點座標(Y軸)
sx         源影象的原點座標(X軸)
sy         源影象的原點座標(Y軸)
sh         源影象的大小(高度)
sw         源影象的大小(寬度)
dw         目標影象的大小(寬度)
dh         目標影象的大小(高度)
image      繪製到canvas上面的影象(HTMLImageElement)

說明

[1] drawImage方法的第一個引數可以是HTMLImageElement型別的影象或HTMLCanvasElement型別的Canvas物件
    或者HTMLVideoElement型別的視訊物件。
[2] drawImage方法可以將一幅影象(Canvas物件 」視訊幀)的整體或部分繪製到Canvas中,在繪製到畫布的時候可以
    任意指定繪製的位置以及縮放的比例。

圖示

示例-01

<canvas id="canvas" width="800" height="1000"></canvas>

<script>

  //[1] 獲取畫布和對應的上下文
  var ctx  = document.getElementById("canvas").getContext("2d");

  //[2] 建立Image影象並設定資料來源
  var img  = new Image();
  img.src = "PQ.png";
  img.alt = "我是一頭小豬,我全家都是豬~";

  //[3] 監聽圖片載入完畢,繪製圖片到畫布
  img.onload = function(){

    //演示001
    //把圖片繪製到Canvas畫布上,繪製參考的原點座標為(0,0),等比例縮放圖片大小至寬高均為200
    ctx.drawImage(img,0,0,200,200)

    //演示002
    //剪下(剪下起點的參考座標為[150,0])圖片的一部分(右半邊)繪製到畫布上,繪製參考的原點座標為(205,0)
    ctx.drawImage(img,150,0,150,300,205,0,150,300)

    //演示003
    //把圖片(300 * 300)繪製到畫布的指定位置,繪製參考的原點座標為(360,0)
    ctx.drawImage(img,360,0);
  }
  
</script>

示例-02

<canvas id="canvas" width="2000" height="800"></canvas>

<script>

  //[1] 獲取頁面中的Canvas和對應的上下文
  var ctx  = document.getElementById("canvas").getContext("2d");

  //[2] 建立Image影象並設定資料來源
  var img  = new Image();
  img.src  = "hero.png";

  //[3] 定義變數(源圖片的寬度、高度、等分數量)
  var width  = 1620,height = 240,equalDivisionCount = 7;

  //[4] 監聽Image影象的載入
  img.onload = function(){

    //[5] 等Image影象載入完成後先把完整的影象繪製到畫布
    ctx.drawImage(img,0,0)

    //[6] 通過定時器來控制影象的繪製(動畫)
    var i = 0;
    var timer = setInterval(function () {

      ctx.canvas.width = 2000;
      ctx.drawImage(
          img,
          width * i/equalDivisionCount,0,
          width/equalDivisionCount,height,
          width * i/equalDivisionCount,0,
          width/equalDivisionCount,height
      );
      i++;

      if(i == 7)
      {
        clearInterval(timer);
        ctx.drawImage(img,0,0)
      }
    },200);
  }
</script>

把Canvas畫布轉換為影象

語法 canvas.toDataURL()

作用 該方法用於將Canvas畫布的內容轉換為影象。

示例

<canvas id="canvas" width="200" height="200"></canvas>
<img src="" alt="">

<script>

  //[1] 獲取頁面中的img標籤
  var oImage = document.getElementsByTagName("img")[0];
  //[2] 獲取頁面中的canvas標籤
  var canvas = document.getElementById("canvas");
  //[3] 獲取畫布的上下文物件
  var ctx    = canvas.getContext("2d");
  
  //[4] 建立Image圖片物件
  var img    = new Image();
  //[5] 設定Image影象的資料來源
  img.src    = "PQ.png";
  //[6] 監聽Image影象的載入
  img.onload = function () {
    
    //[7] 把影象繪製到Canvas畫布中
    ctx.drawImage(img, 0,0,200,200);
    
    //[8] 把畫布轉換為影象儲存並顯示
    oImage.src = canvas.toDataURL();
  }
  
</script>

Canvas標籤物件的toDataURL方法把畫布轉換為Base64表示的影象。

操作影象畫素的方法

getImageData

語法 ctx.getImageData(dx,dy,w,h)

作用 獲取(複製)Canvas畫布上指定矩形區域的畫素資料。

putImageData

語法 ctx.putImageData(imgData,dx,dy,[dirtyX],[dirtyY],[dirtyWidth],[dirtyHeight]);

作用 該方法用於將指定ImageData物件的影象資料放回到畫布上。

引數

imgData         規定要放回畫布的ImageData物件
dx              繪製到畫布的位置(X座標),以畫素計。
dy              繪製到畫布的位置(Y座標),以畫素計。
dirtyX          部分擷取imageData物件的位置(X座標),預設為0。
dirtyY          部分擷取imageData物件的位置(Y座標),預設為0。
dirtyWidth      部分擷取imageData物件的大小(寬度),預設為整幅影象的寬度。
dirtyHeight     部分擷取imageData物件的大小(高度),預設為整幅影象的高度。

說明 putImageData()方法的後四個引數是可選的,在呼叫時要麼傳遞3個引數要麼傳遞7個引數。

示例

<canvas id="canvas" width="1000" height="400"></canvas>
<script>

  //[1] 獲取頁面中的Canvas畫布
  var canvas = document.getElementById("canvas");
  //[2] 獲取Cnavas的上下文物件
  var ctx    = canvas.getContext("2d");

  //[3] 在畫布指定位置繪製一個填空矩形
  ctx.fillStyle="#f9f";
  ctx.fillRect(10,10,100,50);

  //演示001
  //[4] 獲取畫布中指定矩形區域的影象資料,然後再放到畫布的指定位置(相當於複製)
  ctx.putImageData(ctx.getImageData(10,10,100,50),120,10);
  ctx.putImageData(ctx.getImageData(10,10,100,50),230,10,0,0,50,50);

  //[5] 建立Image影象
  var img = new Image();
  //[6] 設定Image影象的資料來源
  img.src = "Yu.jpg";
  
  //[7] 監聽Image影象的載入
  img.onload = function () {
    
    //[8] 把影象繪製到Canvas畫布中
    ctx.drawImage(img,10,70,100,60);

    //演示002
    //[9] 獲取畫布中指定矩形區域的影象資料,然後再放到畫布的指定位置(測試引數)
    var imgData = ctx.getImageData(10,70,100,60);
    /*
    * 3個引數的情況
    * 第一個引數:imageData物件
    * 第二個引數:繪製到畫布的位置(X)
    * 第三個引數:繪製到畫布的位置(Y)
    * */
    ctx.putImageData(imgData,120,70);

    /*
    * 7個引數的情況
    * 第一個引數:imageData物件
    * 第二個引數:繪製到畫布的位置(X)
    * 第三個引數:繪製到畫布的位置(Y)
    * 第四個引數:部分擷取imageData物件的位置(X)
    * 第五個引數:部分擷取imageData物件的位置(Y)
    * 第六個引數:部分擷取imageData物件的大小(寬度)
    * 第七個引數:部分擷取imageData物件的大小(高度)
    * */

    ctx.putImageData(imgData,230,70,50,0,100,60);
    ctx.putImageData(imgData,340,70,0,30,100,60);
    ctx.putImageData(imgData,450,70,0,0,100,30);
    ctx.putImageData(imgData,560,70,0,0,100,60);
  }

</script>

在指定Canvas偏移量的時候,需要以CSS畫素為單位,然而在指定影象資料中矩形區域時需要以設定畫素為單位。此外,需要注意putImageData的後四個引數確定的區域被稱為髒矩形(dirty rectangle),當瀏覽器將髒矩形賦值到Canvas畫布的時候,會預設將裝置畫素轉換為CSS畫素。

二、ImageData物件

在上文中介紹的getImageData()方法,其返回的是ImageData型別的物件,該物件包含widthheight以及data等三個屬性。其中width代表的是以裝置畫素(device pixel)為單位的影象資料寬度,而height相應的代表著資料的高度,此外data是包含著各個裝置畫素數值的陣列。

ImageData物件中,data屬性所包含的每個陣列元素,均對應表示影象資料中的相應畫素值。

每個畫素中都存在四方面的資訊,分別代表當前畫素的顏色(RGB - Red 」Green 」Blue)和透明度(A - alpha)。這些資訊都使用包含8個二進位制位(2的八次方)的整數來表示,取值範圍為 0 ~ 255

也就是說,
ImageData.data[0]代表的是紅色數值,
ImageData.data[1]代表的是綠色數值,
ImageData.data[2]代表的是藍色數值,
ImageData.data[3]代表的是透明度數值。

迴圈往復(如果資料陣列的長度為n,那麼`ImageData.data[n-4]`代表紅色數值,其它的類推)。

createImageData方法

語法

  • ctx.createImageData(w,h);
  • ctx.createImageData(imageDataOther);

作用 該方法根據指定的寬高(目標物件的寬高)來建立新的空白的ImageData物件。

引數

w               指定的寬度。
h               指定的高度。
imageDataOther  參考的影象資料。

示例-01

<canvas id="canvas" width="600" height="400"></canvas>

<script>

  //[1] 獲取頁面中的Canvas畫布和對應的上下文
  var canvas = document.getElementById("canvas");
  var ctx    = canvas.getContext("2d");

  //[2] 建立ImageData物件
  var imageData = ctx.createImageData(2,2);
  console.log(imageData);
  /*
   * data: Uint8ClampedArray(16) [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
   * height: 2
   * width:  2
   */

  //[3] 在畫布中繪製紅色矩形
  ctx.fillStyle = "red";
  ctx.fillRect(0,0,20,20);

  //[4] 獲取畫布中指定區域的影象資料
  imageData = ctx.getImageData(0,0,2,2);
  console.log(imageData);
  /*
   * data: Uint8ClampedArray(16) [255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255]
   * height: 2
   * width:  2
   */

</script>

新物件的預設畫素值transparent black,表示為(0, 0 , 0 , 0)。其中前三項代表的是顏色,最後一項代表的是透明度,color/alpha以陣列形式存在,而陣列的大小為ImageData物件的四倍。

示例說明

在上面的程式碼中ctx.createImageData(2,2)表示要建立2 * 2區域的空白ImageData物件。
呼叫方法後得到的ImageData物件擁有 2 * 2 = 4個畫素,每個畫素由陣列中的四個元素表示。

列印ImageData屬性得到的結果([4])顯示為:
data:Uint8ClampedArray(16) [255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255]
我們觀察下標0~3的這組資料:255,0,0,255,嘗試給出標註結果為255(Red),0(Green),0(Blue),255(alpha)

通過上面的分析,在掌握ImageData內部表示結構後,我們發現通過程式碼完全可以精準的控制影象的任何一個畫素的顯示,包括該畫素的RGB顏色和透明度均可以控制,結合相應的演算法和計算公式就可以簡單的實現任何濾鏡效果。

示例-02

<canvas id="canvas" height="400" width="600"></canvas>

<script>

  //[1] 獲取畫布和繪圖上下文
  var canvas  = document.getElementById("canvas");
  var ctx     = canvas.getContext("2d");

  //[2] 建立ImageData物件(10 * 10)
  var imgData = ctx.createImageData(10,10);

  //[3] 設定ImageData影象使用紅色填充
  var length  = imgData.data.length;
  for (var i = 0; i<length; i+=4)
  {
    imgData.data[i+0] = 255;
    imgData.data[i+1] = 0;
    imgData.data[i+2] = 0;
    imgData.data[i+3] = 255;
  }

  //[4] 把影象資料繪製到畫布上面
  ctx.putImageData(imgData,0,0);

</script>