Canvas API總結

Precipice君發表於2019-04-01

概述

Canvas(畫布)用於在網頁實時生成影象,並且可以操作影象內容,基本上它是一個可以用JavaScript操作的點陣圖(bitmap)。

開始使用


  1. 新建一個<canvas>網頁元素。
<canvas id="myCanvas" width="400" height="200">
你的瀏覽器不支援canvas!
</canvas>
複製程式碼
  • 替換內容
    • 支援<canvas>的瀏覽器將會忽略在容器中包含的內容,正常渲染canvas。
    • 不支援<canvas>的瀏覽器會顯示代替內容
  • widthheight
    • 預設寬度為300畫素,預設高度為150畫素。
    • html屬性設定width/height時隻影響畫布本身不影畫布內容
    • css屬性設定width/height時不但會影響畫布本身的高寬,還會使畫布中的內容等比例縮放(縮放參照於畫布預設的尺寸)
  1. <canvas>元素只是創造了一個固定大小的畫布,要想在它上面去繪製內容,需要用getContext()方法找到它的渲染上下文
//獲取canvas容器
var canvas = document.getElementById('myCanvas');
//建立一個渲染上下文
if (canvas.getContext) {
  var ctx = canvas.getContext('2d');
}
複製程式碼

上面程式碼中,getContext方法指定引數2d,表示該canvas節點用於生成2D圖案(即平面圖案)。如果引數是webgl,就表示用於生成3D影象(即立體圖案),這部分實際上單獨叫做WebGL API。

繪圖方法

canvas畫布提供了一個用來作圖的平面空間,該空間的每個點都有自己的座標,x表示橫座標,y表示豎座標。原點(0, 0)位於影象左上角,x軸的正向是原點向右,y軸的正向是原點向下

樣式和顏色

  • fillStyle :設定填充顏色,預設黑色
  • strokeStyle :設定輪廓顏色,預設黑色
  • lineWidth :設定線條寬度,屬性值為任意正整數,預設值是1.0。
  • lineJoin:控制線條相交的方式(預設是miter
    • round : 圓角
    • bevel : 斜角
    • miter : 直角
  • lineCap:線段末端的形狀(預設值是 butt)
    • butt :線段末端以方形結束。
    • round :線段末端以圓形結束
    • square:線段末端以方形結束,但是增加了一個寬度和線段相同,高度是線段厚度一半的矩形區域

save()、restore()、beginPath()

  • 樣式容器
    • 每次呼叫樣式api時,都會往樣式容器裡做登記
    • 呼叫save()時,將樣式容器中的狀態壓棧,儲存上下文環境
    • 呼叫restore()時,將樣式棧的棧頂狀態彈出到樣式容器中,恢復到上一次儲存的上下文環境
//用save方法,儲存了當前設定
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);

//使用restore方法,恢復了儲存前的設定
ctx.restore();

//繪製了一個沒有陰影的矩形
ctx.fillStyle = '#000000';
ctx.fillRect(180,10,150,100);
複製程式碼
  • 路徑容器
    • 每次呼叫路徑api時,都會往路徑容器裡做登記
    • 呼叫beginPath時,清空整個路徑容器
ctx.save();
//關於樣式的設定
ctx.beginPath();
//關於路徑的設定
ctx.restore();
複製程式碼

變換

  • translate(x, y):將canvas座標原點移動到(x,y),translate是累加
  • rotate(angle):圍繞原點旋轉影象angle弧度(順時針),rotate是累加
  • scale(x, y):縮放影象;x和y分別是橫軸和縱軸的縮放因子(正值),scale是累加
    • 比 1.0 小:縮小
    • 比 1.0 大:放大
    • 為 1.0 時什麼效果都沒有

(1)繪製矩形

//填充矩形(x, y是橫縱座標,原點在canvas的左上角)
ctx.fillRect(x, y, width, height);
//邊框矩形,預設1px 黑色。   
ctx.strokeRect(x, y, width, height);
//清除指定的矩形區域,變為透明
ctx.clearRect(x, y, width, height);       //繪製動態效果時,常用來清除整個畫布
複製程式碼
  • rect(x, y, width, height):繪製矩形路徑

(2)繪製路徑

//新建路徑,beginPath是繪製新圖形的開始
ctx.beginPath()
//路徑(線)的起點,一般在上面這條命令後執行
ctx.moveTo(x, y)  
//線的終點
ctx.lineTo(x, y)   
//閉合路徑,不是必須的,如果線的終點跟起點一樣,會自動閉合。
ctx.closePath()
//通過線條繪製輪廓(邊框)
ctx.stroke() //不會自動呼叫closePath()  
//通過路徑填充區域(實心)
ctx.fill()  //自動呼叫closePath()
複製程式碼

例:繪製一個三角形

ctx.beginPath();
ctx.moveTo(75, 50);    //路徑起點
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();   //自動將路徑閉合,並預設填充黑色。
複製程式碼
  • quadraticCurveTo(cp1x, cp1y, x, y):繪製二次貝塞爾曲線,cp1x,cp1y為一個控制點,起始點為moveto時指定的點,x,y為結束點。
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):繪製三次貝塞爾曲線,cp1x,cp1y為控制點一,cp2x,cp2y為控制點二,起始點為moveto時指定的點,x,y為結束點。
  • ctx.isPointInPath(x, y):判斷在當前路徑中是否包含檢測點
    • x:檢測點的X座標
    • y:檢測點的Y座標
    • 注意,此方法只作用於最新畫出的canvas影象

(3)繪製文字

  • fillText(string, x, y):在指定的(x,y)位置填充指定的文字
    • 4個引數:文字字串、起點的x座標、y座標、可選的最大畫素寬度。
  • strokeText(string, x, y):在指定的(x,y)位置填充指定的文字
// 設定字型,必須要有大小和字型
ctx.font = "Bold 20px Arial"; 
// 設定對齊方式,可選值包括: left, right  center
ctx.textAlign = "left";
// 設定填充顏色
ctx.fillStyle = "#008600"; 
// 設定字型內容,以及在畫布上的位置
ctx.fillText("Hello!", 10, 50); 
// 繪製空心字
ctx.strokeText("Hello!", 10, 100); 
複製程式碼
  • measureText() 方法:返回一個TextMetrics 物件,包含關於文字尺寸的資訊(例如文字的寬度)

(4)繪製圓形和扇形

//繪製圓形
ctx.arc(x, y, r, start, end, true/false)   //(x, y)圓心,r半徑,start和end是開始和結束角度,false表示順時針(預設),true表示逆時針。
//繪製弧線
ctx.arcTo(x1, y1, x2, y2, r);    //當前端點、(x1,y1)和(x2,y2)這三個點連成的弧線,r是半徑。
複製程式碼

例:繪製實心的圓形

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)設定陰影

ctx.shadowOffsetX = 10; // 設定水平位移
ctx.shadowOffsetY = 10; // 設定垂直位移
ctx.shadowBlur = 5; // 設定模糊度
ctx.shadowColor = "rgba(0,0,0,0.5)"; // 設定陰影顏色

ctx.fillStyle = "#CC0000"; 
ctx.fillRect(10,10,200,100);
複製程式碼

(6)設定漸變

  • createLinearGradient(x1, y1, x2, y2):建立一個canvas漸變(線性漸變)
    • 漸變起點 (x1,y1) 與終點 (x2,y2)
  • gradient.addColorStop(position, color)
    • gradient:createLinearGradient的返回值
    • addColorStop 方法接受 2 個引數:
      • position 引數必須是一個 0.0 與 1.0 之間的數值,表示漸變中顏色所在的相對位置,例如,0.5 表示顏色會出現在正中間。
      • color 引數必須是一個有效的 CSS 顏色值,如 #FFF, rgba(0,0,0,1)等等
//建立一個canvas線性漸變,返回`CanvasGradient`物件的例項
var myGradient = ctx.createLinearGradient(0, 0, 0, 160); 

myGradient.addColorStop(0, "#BABABA"); 

myGradient.addColorStop(1, "#636363");
//繪製漸變填充的矩形
ctx.fillStyle = myGradient;
ctx.fillRect(10,10,200,100);
複製程式碼
  • createRadialGradient(x1, y1, r1, x2, y2, r2):canvas漸變(徑向漸變)
    • 前三個引數則定義另一個以(x1,y1) 為原點,半徑為 r1 的圓
    • 後三個引數則定義另一個以 (x2,y2) 為原點,半徑為 r2 的圓

影象處理方法

drawImage()方法

Canvas API 允許將影象檔案插入畫布,做法是讀取圖片後,使用drawImage方法在畫布內進行重繪。

  • canvas操作圖片時,必須要等圖片載入完才能操作
  • drawImage(image, x, y, width, height)
    • image:影象檔案的DOM元素
    • xy :繪製該影象的起始座標
    • widthheight:目標寬高
var image = new Image();
image.src = 'image.png';

image.onload = function() {
  var canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;
  canvas.getContext('2d').drawImage(image, 0, 0);
  // 插入頁面底部
  document.body.appendChild(image);
  return canvas;
}


複製程式碼

createPattern()方法:設定背景模式

  • createPattern(image, repetition)
    • image:影象源
    • epetition
      • repeat
      • repeat-x
      • repeat-y
      • no-repeat

一般情況下,我們都會將fillstyle的值設定為createPattern返回的物件,只表示在某個特定的區域內顯示重複的影象。

getImageData()方法,putImageData()方法

通過getImageData方法和putImageData方法,可以處理每個畫素,進而操作影象內容。

  • ctx.getImageData(sx, sy, sw, sh):獲得一個包含畫布場景畫素資料的ImageData物件,它代表了畫布區域的物件資料
    • 引數
      • sx:要被提取的畫面區域的 x 座標。
      • sy:要被提取的畫面區域的 y 座標。
      • sw:要被提取的畫素寬度。
      • sh:要被提取的畫素高度。
    • ImageData物件中儲存著canvas物件真實的畫素資料,它包含以下幾個只讀屬性:
      • width:圖片寬度,單位是畫素
      • height:圖片高度,單位是畫素
      • data:Uint8ClampedArray型別的一維陣列, 包含著RGBA格式的整型資料,範圍在0至255之間(包括255)
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
複製程式碼
  • putImageData(myImageData, dx, dy):把影象資料繪製到畫布上
context.putImageData(imageData, 0, 0);
複製程式碼
  • 套路程式碼

假定filter是一個處理畫素的函式,那麼整個對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);

}
複製程式碼

createImageData()方法

  • ctx.createImageData(width, height):建立一個ImageData物件
    • width : ImageData 新物件的寬度。
    • height: ImageData 新物件的高度。
    • 預設建立出來的是透明的

合成

  • globalAlpha = value:設定全域性透明度,這個屬性影響到 canvas 裡所有圖形的透明度
    • 有效的值範圍是 0.0 (完全透明)到 1.0(完全不透明)
    • 預設是 1.0
  • globalCompositeOperation:覆蓋合成(source--->新的影象(源);destination--->已經繪製過的圖形(目標))
    • source-over(預設值):源在上面,新的影象層級比較高
    • source-in :只留下源與目標的重疊部分(源的那一部分)
    • source-out :只留下源超過目標的部分
    • source-atop:砍掉源溢位的部分
    • destination-over:目標在上面,舊的影象層級比較高
    • destination-in:只留下源與目標的重疊部分(目標的那一部分)
    • destination-out:只留下目標超過源的部分
    • destination-atop:砍掉目標溢位的部分

toDataURL():將畫布匯出為影象

注意是canvas元素介面上的方法

function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL('image/png');
  return image;
}
複製程式碼

上面的程式碼將Canvas資料,轉化成PNG data URI。

常見效果

灰度效果

灰度圖(grayscale)就是取紅、綠、藍三個畫素值的算術平均值。假定d[i]是畫素陣列中一個象素的紅色值,則d[i+1]為綠色值,d[i+2]為藍色值,d[i+3]就是alpha通道值。轉成灰度的演算法,就是將紅、綠、藍三個值相加後除以3,再將結果寫回陣列。

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;

};
複製程式碼

復古效果

復古效果(sepia)則是將紅、綠、藍三個畫素,分別取這三個值的某種加權平均值,使得影象有一種古舊的效果。

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;

};
複製程式碼

相關文章