HTML5 Canvas(基礎知識)

最近筆者在學習HTML5的新元素<canvas>,會分享一些基礎知識以及小例子,最終使用<canvas>實現一個繪製簡單圖表(條形圖、線圖或者餅圖)的js庫,會更新一到兩篇文章~
下面咱們開始吧~css

確認寬度和高度

咱們首先應該指定<canvas>標籤即畫布的寬度和高度屬性,並在開始和閉合標籤之間添加後備信息:html

<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <canvas id="canvas" width="500" height="500">Canvas is not supported.</canvas>
  </body>
</html>

取得繪圖上下文

調用canvasgetContext()方法,這個方法接收一個參數,即上下文的名字:前端

var canvas = document.getElementById("canvas");
if(canvas.getContext) {
  var context = canvas.getContext("2d")
}

上述例子中,咱們在調用getContext()方法時,首先檢測其是否存在,這是因爲有的瀏覽器遇到HTML規範以外的標籤時,也會建立一個DOM對象,好比咱們在Chrome中嘗試如下代碼:html5

<ppp id="ppp"></ppp>
document.getElementById("ppp");
//<ppp id="ppp"></ppp>

所以即便當前瀏覽器不能兼容HTML規範中的canvas元素,一樣會建立DOM對象,可其中卻不存在getContext()
context上下文對象中包含了繪圖須要的一系列屬性和方法,你們在閱讀本文時,記得區分屬性和方法,改變屬性會影響到後續的繪圖效果,而調用方法每每是一次性的。git

繪製簡單的2D圖形

座標原點

2D上下文的座標默認開始於左上角,原點座標爲(0, 0),使用translate(x, y)能夠更改座標原點。github

填充

填充,就是用指定的樣式,好比顏色、漸變或圖像填充指定區域,對應的上下文屬性爲fillStyle,默認值爲#000000, 好比:canvas

context.fillStyle = "orange";
描邊

描邊就是在指定區域邊緣畫指定樣式的線,好比:瀏覽器

context.strokeStyle = "grey";
繪製矩形

矩形是惟一一個能夠在2D上下文中直接話的形狀,其餘的都須要繪製路徑,可使用3個方法直接繪製矩形。ide

fillRect(x, y, width, height)

能夠繪製一個由fillStyle指定填充樣式的矩形,好比:工具

context.translate(100, 100)
context.fillStyle = "#99cccc";
context.fillRect(-100, -50, 200, 100);
context.fillStyle="#3399cc";
context.fillRect(-60, -30, 120, 60);

strokeRect(x, y, width, height)

能夠繪製一個由strokeStyle lineWidth lineCap lineJoin等屬性指定描邊樣式的矩形,好比:

context.strokeStyle = "#99cccc";
context.lineWidth = "50";
context.lineJoin = "bevel";
context.strokeRect(0, 0, 400, 200);

屬性名 含義 取值
lineCap 線條末端的形狀 butt平頭round圓頭square方頭
lineWidth 線條寬度 整數
lineJoin 線條相交的方式 round圓交bevel斜交mitter斜接
clearRect(x, y, width, height)

能夠清楚畫布上的指定區域,好比第一個例子中的兩個矩形,咱們將中間一小塊清除:

context.translate(100, 100)
context.fillStyle = "#99cccc";
context.fillRect(-100, -50, 200, 100);
context.fillStyle="#3399cc";
context.clearRect(-60, -30, 120, 60);

繪製路徑

使用路徑咱們能夠繪製出比較複雜的圖形,開始繪製前,首先執行:

context.beginPath();

結束繪製時,執行:

context.closePath();

如下咱們列舉了繪製路徑的幾個方法:

直線

lineTo(x, y),從當前遊標至(x, y)畫一條直線。

移動遊標

moveTo(x, y) 將遊標移動至(x, y),移動過程當中不畫線。好比:

context.beginPath();
context.moveTo(10, 10);
context.lineTo(50, 40);
context.moveTo(50, 50);
context.lineTo(100, 90);
context.stroke();
context.closePath();

弧線或圓形
arc(x, y, radius, startAngle, endAngle, counterclockwise)

從startAngle到endAngle繪製一條以(x, y)爲圓心,radius爲半徑的弧線,其中startAngle和endAngle用弧度表示,couterclockwise爲false時,順時針畫弧線, 反之,逆時針畫弧線。

var canvas = document.getElementById("canvas");
if(canvas.getContext) {
  var context = canvas.getContext("2d");
  context.beginPath();
  context.arc(400, 400, 50, arcUnit()*30, arcUnit()*180, false);
  context.stroke();
}
function arcUnit() {
  return Math.PI/180;
}

arcTo(startX, startY, endX, endY, radius)

arcTo()方法將利用當前座標、起點(startX,startY)和終點(endX,endY)這三個點所造成的夾角,繪製一段與夾角的兩邊相切而且半徑爲radius的圓上的弧線。弧線的起點就是當前座標所在邊與圓的切點,弧線的終點就是終點(endX,endY)所在邊與圓的切點,而且繪製的弧線是兩個切點之間長度最短的那個圓弧。此外,若是當前端點不是(startX,startY),arcTo()方法還將添加一條當前端點到(startX,startY)的直線線段。
若是你們還記得高中數學的話,咱們應該能夠猜到,使用這個方法畫弧線大體有三種狀況:
** 只有一種半徑可使弧線兩端正好在起點和終點
** 若是半徑過大,畫不出弧線
** 若是半徑較小,一定會有一條當前端點到起點的直線
咱們舉三個例子:
首先定義一些通用的

var context = canvas.getContext("2d");
var currentPoint = {
    x: 0,
    y: 0
};
var startPoint = {
    x: 50, 
    y: 50
};
var endPoint = {
    x: 100,
    y: 0
};

而後繪製參考線

context.moveTo(currentPoint.x, currentPoint.y);
context.lineTo(startPoint.x, startPoint.y);
context.lineTo(endPoint.x, endPoint.y);
context.strokeStyle = "red";
context.stroke();

畫第一條弧線

context.moveTo(currentPoint.x, currentPoint.y);
context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 80);
context.strokeStyle = "grey";
context.stroke();

context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 120);

context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 40);

曲線
二次貝塞爾曲線

quadraticCurveTo(cpX, cpY, x, y)
上述方法能夠畫一條從當前位置到(x, y), 以(cpX, cpY)爲控制點的貝塞爾曲線

三次貝塞爾曲線

bezierCurveTo(cpX1, cpY1, cpX2, cpY2, x, y)
上述方法能夠畫一條從當前位置到(x, y), 以(cpX1, cpY1)和(cpX2, cpY2)爲控制點的貝塞爾曲線。

二次貝塞爾與三次貝塞爾的繪製涉及到比較複雜的數學運算,筆者在此就忽略啦...
固然,大部分前端人士可能都跟筆者同樣,只但願能畫出一條優美的曲線,並不關心實現細節,那麼通常認爲何樣的曲線纔算是優美的曲線呢:

  1. 對稱的曲線
  2. 有一個適度的弧度

根據以上規則,咱們寫一個工具方法:

function drawCurvePath( ctx, start, end, curveness ) {
  var cp = [
    ( start.x + end.x ) / 2 - ( start.y - end.y ) * curveness,
    ( start.y + end.y ) / 2 - ( end.x - start.x ) * curveness
  ];
  ctx.moveTo( start.x, start.y );
  ctx.quadraticCurveTo( 
    cp[ 0 ], cp[ 1 ],
    end.x, end.y
  );
  ctx.stroke();
}

以上參考自用canvas繪製一個曲線動畫——深刻理解貝塞爾曲線,你們能夠前往瞭解更深刻的貝塞爾曲線畫法。

矩形

使用rect(x, y, width, height)能夠繪製一個左上角座標爲(x, y),寬width,高height的矩形路徑。

context.rect(300, 300, 100, 200);
context.stroke();

繪製文本

在繪製本文以前,若是有必要,咱們首先應該指定context的幾個屬性, 好比:

context.font = "bold 14px Arial"; // 格式同css中指定字體樣式
context.textAlign = "center"; // start end center left right
context.textBaseline = "middle"; // top hanging middle alphabetic ideographic bottom

fillText(text, x, y, maxWidth)使用fillStyle屬性顯示文本,strokeText(text, x, y, maxWidth)使用strokeStyle屬性爲文本描邊。
使用measureText(text)方法能夠得到文本的寬度。若是咱們並不清楚指定的寬度夠不夠顯示當前字體設置下的一段文字,可使用以下方法:

var fontSize = 50;
var maxWidth = 100;
context.font = "bold " + fontSize+"px Arial";
var text = "Hello World!";
while(context.measureText(text).width > maxWidth) {
    fontSize--;
    context.font = "bold " + fontSize+"px Arial";
}
context.fillText(text, 50, 50, maxWidth);

變換

旋轉

rotate(angle)

context.rotate(Math.PI/4)
context.fillText(text, 50, 50, maxWidth);

縮放

scale(scaleX, scaleY)

context.scale(1.2, 1.2);
context.fillText(text, 50, 50, maxWidth);
context.scale(0.5, 0.5);
context.fillText(text, 50, 50, maxWidth);

移動座標原點

translate(x, y)
假如咱們要繪製一個對稱圖形,移動座標原點將會大大簡化對座標的計算。

矩陣變換

使用transform(scaleX,skewX,skewY,scaleY,transX,transY)能夠進行矩陣變換,其實以上講的三個方法本質上都在調用矩陣變換,從參數名中能夠看出,它們分別表示X軸方向的縮放,Y軸方向的縮放,X軸方向的斜切,Y軸方向的斜切,X軸方向的偏移量,Y軸方向的偏移量。默認值分別爲1 0 0 1 0 0。
咱們如今使用transform()從新定義一遍以前的三個方法:
rotate

function rotate (ctx, degree) {
var unit = Math.PI/180;
ctx.transform(Math.cos(degree*unit),Math.sin(degree*unit),-Math.sin(degree*unit),Math.cos(degree*unit),0,0)
}

scale

function scale (ctx, scale) {
ctx.transform(scale.x, 0, 0, scale.y, 0, 0);
}

translate

function translate (ctx, translate) {
ctx.transform(1, 0, 0, 1, translate.x, translate.y);
}

咱們如今繪製一段文本,先平移(50, 50),而後縮放2倍,最後旋轉30度。

context.fillText(text, 50, 50, maxWidth);
translate(context, {x: 50, y: 50});
scale(context, {x: 2, y: 2});
rotate(context, 30);
context.fillText(text, 50, 50, maxWidth);


每次執行transform()都是基於上一次的結果,並非初始狀態,不少時候咱們想執行的是基於初始狀態旋轉、平移或縮放,而非上一次的狀態,此時咱們可使用setTransfrom(scaleX,skewX,skewY,scaleY,transX,transY),此方法首先會重置變換矩陣,而後執行transform()
更加詳細的內容能夠參考html5 Canvas畫圖教程26:用transform來實現位移,縮放,旋轉等

繪製圖像

使用drawImage(image, x1, y1, width1, height1, x2, y2, width2, height2)能夠將圖像的指定部分按照指定的大小繪製到畫布指定的位置上。

參數 含義
image 要繪製的圖像,能夠是HTMLImageElement,也能夠是canvas
x1 源圖像的x座標
y1 源圖像的y座標
width1 源圖像的寬度
height1 源圖像的高度
x2 畫布的x座標
y2 畫布的y座標
width2 圖像在畫布上顯示的寬度
height2 圖像在畫布上顯示的高度

繪製陰影

若是要給形狀或路徑加上陰影,咱們要在繪製前設置context對象的如下屬性:

屬性 含義
shadowColor 陰影顏色
shadowOffsetX x軸偏移量
shadowOffsetY y軸偏移量
shadowBlur 模糊的像素數
context.shadowColor = "grey";
context.shadowOffsetX = "20";
context.shadowBlur = "5";
context.fillText(text, 50, 50, maxWidth);

漸變

線性漸變

使用createLinearGradient(startX, startY, endX, endY)來建立線性漸變,這個方法確認了漸變的起始和方向,而後咱們經過addColorStop(position, color)來添加漸變的顏色,position是0到1的數字。一下筆者畫一個超級喜歡的條紋漸變:

var grad = context.createLinearGradient(50, 50, 200, 200)
grad.addColorStop(0, "grey");
grad.addColorStop(0.3, "grey");
grad.addColorStop(0.3, "red");
grad.addColorStop(0.5, "red");
grad.addColorStop(0.5, "orange");
grad.addColorStop(0.7, "orange");
context.fillStyle = grad;
context.fillRect(50, 50, 150, 150);

徑向漸變

使用createRadialGradient(centerX1, centerY1, radius1, centerX2, centerY2, radius2)建立徑向漸變。

圖像重複

使用createPattern(image, repeatType)能夠繪製重複的圖像,用來填充或描邊,第一個參數是要重複的HTMLImageElement或canvas或video,第二個參數表示如何重複該圖像,可取repeat repeat-x repeat-y no-repeat

相關文章
相關標籤/搜索