canvas 的基本使用


一、canvas的介紹

  canvas是html5出現的新標簽,像所有的DOM對象一樣它有自己本身 的屬性、方法和事件,其中就有繪圖的方法,js能夠調用它來進行繪圖。canvas只有兩個屬性,而且是可選的,width、height,這兩個屬性也可以通過 js 來定義。

  canvas如果沒有定義大小,則默認大小為寬 300px,高 150px。當然使用 css 也是可以設置 canvas 的大小,但如果css設置的寬高比例與 js 或在 標簽的屬性 width、height上設置的比例不一致,則會變形,而且如果css設置的值是它們的n倍,則圖像也會放大n倍。

如下圖第一個是長寬50的紅色矩形,第二個是css設置為js設置的兩倍是得出的圖像,第三個是css設置寬高比為1.5:1時得出的圖像且高是js設置的兩倍。

  canvas在某些較老的瀏覽器(如IE9以下)中並不支持,我們可以在canvas標簽中寫些內容替代當canvas標簽不被支持的時候。也可以使用 getContext 來判斷是否支持canvas。

<!-- 插入替換的圖片或提示文本 -->
<canvas id="canvas" width="500" height="500"> <img src="images/canvas.png" width="500" height="500" alt=""/> // </canvas>
var canvas = document.getElementById('canvas'); if (canvas.getContext) { console.log('你的瀏覽器支持Canvas!'); } else { console.log('你的瀏覽器不支持Canvas!'); }

  canvas.getContext() 方法返回了一個相當於畫筆的“東西”,通過它我們可以進行各種繪畫。當然繪畫時也會考慮是畫2D的還是3D的,這個方法在傳入參數后即可,如下:

var context = canvas.getContext("2d");

如果想畫3D的該怎么辦,這樣:

var context = canvas.getContext("webgl");

本文只討論2D的繪畫的使用。

  畫圖首先要有一個參考系,原點在哪,x、y軸的方向,單位是什么?

如下圖,坐標軸在左上角,x軸為橫向向右,y軸為垂直向下,所以畫圖的坐標都是正值,如果是負值,圖像就超出畫布看不見了。坐標軸單位為px。

 

二、canvas繪制矩形

  畫矩形首先要知道在哪里畫,矩形的大小,邊框的粗細,邊框的顏色,是否是填充的這些要素。而我們的畫筆的API可以做到這些。

  context.strokeRect(10,10,50,50),繪制無填充的矩形,四個參數分別表示:x軸坐標,y軸坐標,長,寬。。

  context.fillRect(10,10,50,50),繪制填充的矩形,默認填充黑色。

var canvas = document.getElementById("canvas"); canvas.width="500"; canvas.height="500"; //第一步必須獲取一個"畫筆" 
var context = canvas.getContext("2d"); //畫矩形 //設置邊框寬度  單位px
context.lineWidth="6"; //設置顏色
context.strokeStyle="red"; context.strokeRect(10,10,50,50) context.fillStyle = '#f00' context.fillRect(10,10,20,20) //清除畫筆的軌跡  將所在區域的矩形內容清空可以理解為橡皮擦
context.clearRect(15,15,20,20)

畫出圖像如下,左上角白色的就是清除掉的部分。

如果你設置了邊框寬度與顏色,但畫出的圖像沒有,則是要在畫之前進行設置,就是在context.strokeRect(10,10,100,100)之前定義,在其之后定義就只對后面畫的有效了。

  還有一個方法可以繪畫矩形:這兩個API要永遠在一起,分開的就無法繪畫了。

context.rect(10,10,100,100); //繪制的形狀
context.stroke()

 

三、canvas繪制圓和弧線 

  context.arc(150, 150, 100, 0, 2*Math.PI, false),繪制圓,六個參數分別是:圓心的x軸坐標,圓心的y軸坐標,半徑,圓弧的起始弧度,圓的結束弧度,繪制的方向(默認為false,順時針方向)。圓弧的起始弧度默認為水平方向直徑與圓弧的右邊交點處,如下圖A點處,結束位置是順時針方向的弧度結束點。360°是2Π,90°是1/2Π。

  如果要繪制實心的圓則要用 context.fill() 表示繪制實心圓,這是你可以不寫 context.stroke() 那么圓就沒有邊框。如果繪制的弧度小於2Π那繪制實心的時候出來的是一個月亮一樣的圖形。繪制圖像如下圖右邊兩張。故如果要繪制弧線則讓所繪制的的弧度之差小於2Π即可。

context.arc(150,150,100,0,2*Math.PI,false); context.strokeStyle="#c33"; //邊框顏色
context.fillStyle="red"; //填充顏色 // context.fill(); //填充
context.stroke(); //畫邊框
 context.arc(150,150,100,.5*Math.PI,1*Math.PI); // 弧度從1/2*Π 到 Π,及90°到180°
context.strokeStyle="#c33"; // context.fill();
context.stroke();

  

 

四、canvas繪制線

  context.moveTo(10,50),將“畫筆”移動到指定的坐標x以及y上,context.lineTo()繪制路徑的結束點。可以設置路徑的粗細與顏色,粗細的單位是px。

 

// 繪制一條線
context.moveTo(10,150); context.lineTo(350,150); context.strokeStyle="black"; context.lineWidth="6"; context.stroke(); 

 

  所的圖像如下,是一條長340px的直線。

  這時自然會想到如果同時繪制多條直線與弧會怎樣,如下代碼:

 

context.moveTo(10,150); context.lineTo(350,150); context.strokeStyle="black"; context.lineWidth="30"; context.stroke(); context.moveTo(50,50); context.lineTo(200,200); context.strokeStyle="green"; context.lineWidth="10"; context.stroke(); context.arc(150,150,100,0.5*Math.PI,1.5*Math.PI,false); context.strokeStyle="#c33"; context.lineWidth="2"; context.stroke();

 

   所得如下圖像,可以看到圖中右很多線,但我們只畫了三條。一條是中間的橫線粗30px,黑色;一條是斜線粗10px,綠色;一條是一個粗1px紅色的半圓。圖上看起來好像有7條線。這是由於在canvas的繪制中,直線是由路徑進行繪制的。

下面的痕跡也可以理解為你拿着畫筆,筆一直沒有離開畫布畫了這三條線。在不同的線之間移動時畫筆也會留下痕跡,故多了那些多余的痕跡。

 

 

五、什么是路徑?

  圖形的基本元素是路徑。路徑是通過不同顏色和寬度的線段或曲線相連形成的不同形狀的點的集合。canvas只支持原生繪制矩形,其他的圖形繪制都至少需要一條生成路徑。

使用路徑繪制圖形需要一些額外的步驟。

  1. 你需要創建路徑起始點。
  2. 然后你使用“畫筆”去畫出路徑。
  3. 把路徑封閉。
  4. 一旦路徑生成,你就能通過描邊或填充路徑區域來渲染圖形。

以下是所要用到的函數:

  1. beginPath()   新建一條路徑,生成之后,圖形繪制命令被指向到路徑上生成路徑。
  2. closePath()   閉合路徑之后圖形繪制命令又重新指向到上下文中。
  3. stroke()     通過線條來繪制圖形輪廓。
  4. fill()      通過填充路徑的內容區域生成實心的圖形。

  調用beginPath()之后,或者canvas剛建的時候,第一條路徑構造命令通常被視為是moveTo()。因為你幾乎總是要在設置路徑的起始位置。生成路徑的步驟:

第一步叫做beginPath()。本質上,路徑是由很多子路徑構成,這些子路徑都是在一個列表中,所有的子路徑(線、弧形、等等)構成圖形。而每次這個方法調用之后,列表清空重置,然后我們就可以重新繪制新的圖形。

第二步就是調用函數指定繪制路徑,本文稍后我們就能看到了。

第三,就是閉合路徑closePath(),不是必需的。這個方法會通過繪制一條從當前點到開始點的直線來閉合圖形。如果圖形是已經閉合了的,即當前點為開始點,該函數什么也不做。

  當你調用fill()函數時,所有沒有閉合的形狀都會自動閉合,所以你不需要調用closePath()函數。但是調用stroke()時不會自動閉合。
 
將上面的代碼閉合一下:
ctx.beginPath(); context.moveTo(10,150); context.lineTo(350,150); context.strokeStyle="black"; context.lineWidth="30"; context.stroke(); ctx.closePath(); ctx.beginPath(); context.moveTo(50,50); context.lineTo(200,200); context.strokeStyle="green"; context.lineWidth="10"; context.stroke(); ctx.closePath(); ctx.beginPath(); context.arc(150,150,100,0.5*Math.PI,1.5*Math.PI,true); context.strokeStyle="#c33"; context.lineWidth="2"; context.stroke(); ctx.closePath();

所得圖像如下:只有三條線了,沒有其他的痕跡了。有人會說為啥半圓的方向變了,細心的朋友肯定發現了,其實是arc()的第六個參數改為 true導致圓弧繪制時的方向改變的緣故。

 

六、函數回調式的寫法

  有時經常書寫畫筆的開始、閉合會很麻煩,這是會想可不可以將這些重復的代碼封裝以下,以后直接調用就行。沒錯就是這樣,這是可以滴。如下:

function draw(cb){ context.beginPath(); context.save(); cb(); context.restore(); context.closePath(); } draw(()=>{ context.moveTo(10,150); context.lineTo(350,150); context.strokeStyle="black"; context.lineWidth="30"; context.stroke(); }) draw(()=>{ context.moveTo(50,50); context.lineTo(200,200); context.strokeStyle="green"; context.lineWidth="10"; context.stroke(); }) draw(()=>{ context.arc(150,150,100,0.5*pi,1.5*pi,true); context.strokeStyle="#c33"; context.lineWidth="2"; context.stroke(); })

如上代碼畫出的圖像和上面的圖是一樣的,有人問這樣寫有什么用,這樣寫在畫復雜圖形時節省了起始與閉合代碼的重復書寫,也很清晰的能看出那些是一個畫筆畫出來的。

這是會有小伙伴問draw函數內的 save()、restore()是干什么的。

 

七、save()、restore()

  用於保存及恢復當前Canvas繪圖環境的所有屬性。其中save()可以保存當前狀態,而restore()可以還原之前保存的狀態。能起到保存繪制狀態和防止污染狀態棧。

  可以用棧來進行理解,當調用save()時會將“畫筆”的狀態(填充風格、線條風格、陰影風格的各種繪圖狀態)壓入一個狀態棧中,每調用一次就壓入一次。當要使用時,用restore()來獲取保存 的“畫筆”狀態,遵循棧的讀取規則,先進后出,你最先取出的是你最后壓入棧中的。如果你調用restore()的次數比save()多,那多余的次數畫出的都是無效的。

  save()只是保存了狀態,並沒有保留之前繪制的圖形。

ctx.fillStyle = 'red'; ctx.fillRect(10,30,15,15); ctx.save(); // 將第一個狀態壓入棧中 
 ctx.fillStyle = 'blue'; ctx.fillRect(50,20,30,30); ctx.save(); // 將第二個狀態壓入棧中 
 ctx.fillStyle = 'green'; ctx.fillRect(100,10,50,50); ctx.save(); // 將第三個狀態壓入棧中 
 ctx.restore(); // 取出棧3(第三個狀態) 
ctx.beginPath(); ctx.arc(200, 35, 30, 0, Math.PI*2, true); ctx.closePath(); ctx.fill(); ctx.restore(); // 取出棧2(第二個狀態) 
ctx.beginPath(); ctx.arc(300, 35, 20, 0, Math.PI*2, true); ctx.closePath(); ctx.fill(); ctx.restore(); // 取出棧1(第一個狀態) 
ctx.beginPath(); ctx.arc(400, 35, 10, 0, Math.PI*2, true); ctx.closePath(); ctx.fill(); ctx.restore(); // 取出棧1(第一個狀態) 
ctx.beginPath(); ctx.arc(400, 35, 10, 0, Math.PI*2, true); ctx.closePath(); ctx.fill();

看出最后多余的一次讀取狀態所繪圖形並沒有顯示出來。所以讀取次數要少於壓入次數。否則無效。

 

八、繪制文字

  文字的繪制很簡單,可以設置字體,大小,顏色,位置。

  字體的位置 第一個number參數時x軸坐標,第二number個為y軸坐標,字體的原點在字體的左下角,如下圖所示位置。

context.strokeStyle="#c33"; //設置顏色,如果要設置實心的字體顏色則要用 fillStyle
context.font="40px 微軟雅黑"; //設置大小與字體
context.fillText("wertantan的博客園",80,80); //實心字體,設置所繪字體內容與位置
context.strokeText("wertantan的博客園",80,150); //空心字體

 

 九、繪制貝塞爾曲線

  二次貝塞爾曲線與三次貝塞爾曲線一般用來繪制復雜有規律的圖形。可以用貝塞爾曲線組合直線、填充與矩形來繪制復雜的圖形。

  quadraticCurveTo(cp1x, cp1y, x, y) 繪制二次貝塞爾曲線,cp1x,cp1y為一個控制點,x,y為結束點。

  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 繪制三次貝塞爾曲線,cp1x,cp1y為控制點一,cp2x,cp2y為控制點二,x,y為結束點。

  有人會問起始點去哪了,起始點可以通過moveTo()來定義,直接移動畫筆到你所要繪制的起始點,之后再繪制貝塞爾曲線的控制點與結束點。每次繪制貝塞爾曲線都可以用moveTo()來定義起始點的位置。

  二次貝塞爾曲線有一個起始點(藍色)、一個結束點(藍色)以及一個控制點(紅色),而三次貝塞爾曲線有兩個控制點。

 

// 二次貝塞爾曲線
context.beginPath();
context.moveTo(50,50);
context.quadraticCurveTo(200,160,50,200);
context.stroke();
context.closePath();

// 三次貝塞爾曲線
context.beginPath();
context.moveTo(50,50);
context.bezierCurveTo(200,80,200,170,50,200)
context.stroke();
context.closePath();

 

 所繪制圖像如下:

       

Canvas除了能繪制基本的形狀和文本,還可以實現動畫、縮放、各種濾鏡和像素轉換等高級操作。這里就不再說了,感興趣的小伙伴可以去 MDN 看一看。

 

至此canvas的基礎使用就結束了,希望各位能有所收獲,如有問題還望指正!

本文所有圖片均由canvas繪制,截取下來。

 

這里附上一個小案例,是一個方形從瀏覽器可視區的左上角移動到右下角的動畫,代碼如下:

 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
    <style>
        *{margin:0;padding:0;} </style>
</head>
<body>
    <canvas id="canvas"></canvas>
</body>
</html>
<script>
  var canvas = document.getElementById("canvas"); var iw = document.documentElement.clientWidth; var ih = document.documentElement.clientHeight; var context = canvas.getContext("2d"); canvas.width = iw-50; canvas.height= ih-50; var x = 0; var y = 0; var b = (ih-50)/(iw-50);

  var timer = setInterval(function(){ context.clearRect(0,0,iw,ih); x++; y+=b; context.fillRect(x,y,50,50*b); if(x>=iw-100){ clearInterval(timer); } console.log(x) },30) </script>

效果如下:

 

 轉載請注明出處!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM