HTML5學習之Canvas基礎知識

CHIEMINCHAN發表於2018-05-05
  • canvas元素本身是沒有繪圖能力的,所有的繪製工作必須在 JavaScript 內部完成。
    var cv = document.getElementById("myCanvas");
    var context = cv.getContext("2d"); //申請一個繪製2d動畫的環境

  • 檢測瀏覽器是否支援:也可以用js指令碼檢查if(canvas.getContext)
    在不支援時可以在canvas標籤文字中插入提示不支援資訊。
    <canvas>hey!你現在的瀏覽器不支援canvas畫布哦!請更新瀏覽器。</canvas> 
    複製程式碼

  • 常規繪製例子
  1. 用canvas繪製直線
    (a) context.moveTo(起點x,起點y)
    (b) context.lineTo(終點x,終點y):從moveTo提供的起點開始,到lineTo的座標繪製一條直線;若沒有moveTo,則lineTo相當於moveTo。若在一個lineTo後再增設lineTo,即從上一lineTo的座標開始向下一lineTo座標延伸直線。
    (c) context.stroke():延給定的座標畫一條直線,也可以指定繪製直線的樣式,顏色粗細等。

  2. 用canvas繪製矩形
    (a) context.fillStyle = "color":填充顏色
    (b) context.fillRect(起點x,起點y,寬,高):矩形起點和大小,或者繪製一個沒有填充顏色只有邊框的矩形 (a) context.strokeStyle = "color" :邊框顏色
    (b) context.strokeRect(起點x,起點y,寬,高) :只描邊不填充

  3. 用canvas繪製圓形
    (a) context.fillStyle = "color";
    (b) context.beginPath():表示開始繪製
    (c) context.arc(圓心座標x,圓心座標y,半徑,開始角度,結束角度,是否為按照給定的開始角度和結束角度的按順時針方向繪製)
    其中:開始角度為0,結束角度為0.5 PI(正下方),1 PI和1.5 PI(正上方),為畫餅圖提供了扇形範圍的依據.第五個引數是弧長Math.PI*2就是整個圓,Math.PI是半圓
    (d) context.closePath():表示結束繪製,會將結束點和開始點通過一點直線連在一起, 即將起點和終點封閉起來,有時可以不要
    (e) context.fill():表示填充圖形,有時可以用context.stroke()代替

例子:

canvas-圓形.png
    for(var i = 0;i<=15;i++){
		context.beginPath();
		context.arc(150,150,i*10,0,Math.PI*0.5,true); //在0開始,順時針Math.PI*0.5結束,即圓正下方結束
		context.stroke(); //不填充而只畫邊框
	}
複製程式碼



canvas-圓形2.png
      for(var i = 0;i<=15;i++){
		context.beginPath();
		context.arc(150,150,i*10,0,Math.PI*1,true);//在0開始,順時針Math.PI結束,即圓正下方半圓結束
		context.stroke(); //不填充而只畫邊框
	}
複製程式碼



canvas-圓形3.png
	for(var i = 0;i<=15;i++){
		context.beginPath();
		context.arc(150,150,i*10,0,Math.PI*1.5,true);//在0開始,順時針Math.PI*1.5結束,即圓正上方結束
		context.stroke(); //不填充而只畫邊框
	    }
複製程式碼



canvas-圓形4.png
	for(var i = 0;i<=15;i++){
		context.strokeStyle = "red"; //設定邊框顏色
		context.beginPath();
		context.arc(150,150,i*10,0,Math.PI*2,true);
		context.stroke(); //不填充而只畫邊框
	   }
複製程式碼



  1. 用canvas繪製三角形
  • //空心三角形
canvas-三角形1.png
	context.beginPath();  //開始繪製路徑
	context.moveTo(100,100); //起點座標設為(100,100)
	context.lineTo(150,150); //下一點的座標設為(150,150)
	context.lineTo(50,150); //下一點的座標設為(50,150)
	context.closePath();//連線起點與終點
	context.stroke(); //繪畫線條
複製程式碼

  • //實心三角形
canvas-三角形2.png
	context.fillStyle = "red"; //設定填充顏色
	context.moveTo(150,50); //設定起點為(150,50)
	context.lineTo(250,250);//下一點的座標設為(150,150)
	context.lineTo(50,250);//下一點的座標設為(50,150)
	context.fill(); //填充顏色,因為會整個影像填充,所以可以有closePath(),也可以沒有
複製程式碼

  • //清除畫布[或某部分]
canvas-三角形3.png
	context.fillStyle = "red";
	context.moveTo(150,50);
	context.lineTo(250,250);
	context.lineTo(50,250);
	context.closePath();
	context.fill();
	
	var clearBtn = document.getElementById("clearCanvas");
	clearBtn.onclick=function(){
	context.clearRect(0,0,150,300); //清除某一部分畫布圖案
	//context.clearRect(起點x,起點y,畫布寬,畫布高);若寬高和整個畫布大小一樣即清除整個畫布
	};
複製程式碼

  1. 繪製二次方貝塞爾曲線(quadraticCurveTo)
canvas-貝塞爾1.png
context.strokeStyle = "black"; //設定線條顏色
context.beginPath(); //開始繪製
context.moveTo(0,200); //定義開始點,從(0,200)開始繪製
//context.quadraticCurveTo(控制點x,控制點y,結束點x,結束點y),
//控制點為與其與開始座標和結束座標連線的兩直線的交點        context.quadratciCurveTo(150,100,300,200);
context.stroke(); //繪製線條
context.globalCompositeOperation = "source-over";
//globalCompositeOperation 屬性一般用於圖形組合,設定如何將一個源(新)影像繪製到目標(已有)的影像上。  
//源影像 = 您打算放置到畫布上的繪圖。目標影像 = 您已經放置在畫布上的繪圖。
//源影像:↓(二次貝塞爾曲線是根據直線部分畫出來的)
context.strokeStyle = "red";
context.beginPath();
context.moveTo(150,100);
context.lineTo(0,200);
context.moveTo(150,100);
context.lineTo(300,200);
context.stroke();	
複製程式碼

  1. 繪製三次方貝塞爾曲線(bezierCurveTo
    參考理解:https://blog.csdn.net/cdnight/article/details/48468653
canvas-貝塞爾2.png

context.strokeStyle = "black";
context.beginPath();  //開始繪製
context.moveTo(0,200); //從(0,200)開始繪製
context.bezierCurveTo(25,50,75,50,300,200);
//context.bezierCurveTo(控制點1座標x,控制點1座標y,控制點2座標x,控制點2座標2,終點座標x,終點座標y)
context.bezierCurveTo(25,50,75,50,300,200);
context.stroke();
context.globalCompositeOperation = "source-over";

context.strokeStyle = "red";
context.beginPath();
//控制點1到開始點的直線
context.moveTo(25,50);
context.lineTo(0,200);
//控制點1到控制點2(三次貝塞爾曲線就是根據這條線畫出來的)
context.moveTo(25,50);
context.lineTo(75,50);
//控制點2到結束點直線
context.moveTo(75,50);
context.lineTo(300,200);
context.stroke();
複製程式碼

  1. 畫布的影像可以按照當前狀態以堆stack的方式儲存下來和按照上一次狀態恢復
    A. save::用來儲存Canvas的狀態。save之後,可以呼叫Canvas的平移、放縮、旋轉、錯切、裁剪等操作.

    B. restore:用來恢復Canvas之前儲存的狀態。防止save後對Canvas執行的操作對後續的繪製有影響.
    注意:save表示儲存save函式之前的狀態,restore表示獲取save儲存的狀態

    例子:

	//矩形一,填充色為橙色,邊框為紅色
	context.fillStyle = "orange";
	context.strokeStyle = "red";
	context.fillRect(20,20,100,100);
	context.strokeRect(20,20,100,100);
	context.fill();
	context.stroke();
	//保持當前canvas狀態
	context.save();
	
	//繪製矩形二,填充色為黃色,邊框色為粉紅色
	context.fillStyle="yellow";
	context.strokeStyle="pink";
	context.fillRect(150,20,100,100);
	context.strokeRect(150,20,100,100);
	context.fill();
	context.stroke();
	
	//恢復上一個矩形的狀態來進行繪畫
	// context.restore();
	context.fillRect(20,150,100,100);
	context.strokeRect(20,150,100,100);
複製程式碼

無context.restore()時的畫面↓:儲存的是最後一次繪畫樣式

canvas-save1.png

有context.restore()時的畫面↓:儲存的是context.save()的繪畫樣式

canvas-save2.png



8. 移動座標空間,繪製漸變傘效果
畫布預設左上角為座標原點,向右為x軸正向,向下為y軸正向。座標空間的單位為畫素,在繪製影像時,可以使用translate來移動座標空間,使座標空間原點移動到指定位置。

例子:

canvas-umbrella.png
var canvasMethod = {
	drawTop:function(context,fillColor){
		context.fillStyle = fillColor;
		context.strokeStyle = fillColor;
		context.beginPath();
		//從座標0,0開始,畫一個半徑為30畫素的正半上方圓
		context.arc(0,0,30,0,Math.PI,true); 
		context.closePath();
		context.fill();
		context.save();//儲存畫布當前狀態,移動位置,fillStyle,strokeStyle為fillColor
	},

	drawStick:function(context){
		context.fillRect(-1.5,0,1,40);//繪製一個矩形,但這個矩形其實是向左偏移1.5畫素,寬為1高為40的一條直線,根據save的樣式進行填充
		context.beginPath();
		context.strokeStyle = "blue";
		context.arc(-5,40,4,Math.PI,Math.PI*2,true);//圓心為(-5,40),半徑為4,順從從PI到2PI的方向,畫的下半圓,即終點在(-1,40)
		context.restore();//採用save中的畫布樣式,即fillStyle,strokeStyle為fillColor
		context.stroke();//根據save的樣式進行描邊
	},

	printUmbrella:function(){
		var context = document.getElementById("canvas1").getContext("2d");
		context.translate(80,80);//第一次移動座標原點到(80,80)
		//繪製十把傘
		for (var i = 0; i < 10; i++) {
			context.save();//儲存畫布上一次狀態,移動位置,fillStyle,strokeStyle預設為黑
			context.translate(60*i,0);//不斷改變座標原點,每次向右移60畫素(圓的直徑)
			this.drawTop(context,"rgb("+(30*i)+","+(255-30*i)+",255)");//繪製半圓並改變填充色彩
			this.drawStick(context);//繪製雨傘柄
			context.restore();//採用畫布save的設定,移動位置,fillStyle,strokeStyle預設為黑
	    }
	}
};

window.onload = function(){
	canvasMethod.printUmbrella();
}
複製程式碼



9. 旋轉座標空間

利用rotate()方法指定一個角度,改變了畫布座標和Web瀏覽器中的Canvas元素的畫素之間的對映,使得任意後續繪圖在畫布中都顯示為旋轉的。但它並沒有旋轉Canvas元素本身。 只有一個引數,為旋轉的角度。
接著上一個雨傘例子,改變canvasMethod.printUmbrella()方法。

canvas-umbrella2.png
printUmbrella:function(){
	var context = document.getElementById("canvas1").getContext("2d");
	context.translate(150,150);//第一次移動座標原點到(150,150)
	
	//繪製8把傘
	for (var i = 0; i < 8; i++) {
		context.save();//儲存畫布上一次的移動狀態
		context.rotate(Math.PI*0.25*i); //2PI/8=0.25PI,每次旋轉0.25PI 
		context.translate(0,-100);//不斷改變座標原點,每次向上移100畫素
		this.drawTop(context,"rgb("+(30*i)+","+(255-30*i)+",255)");//繪製半圓並改變填充色彩
		this.drawStick(context);//繪製雨傘柄
		context.restore();//採用畫布save的設定

	}
}
複製程式碼



10. 縮放圖形

畫布通過scale()去改變canvas上下文物件(context)中橫縱方向的畫素數目來改變圖形的大小。

接收兩個引數分別是x軸方向縮放倍數和y軸方向縮放倍數,都必須為正數,若要比原來大則>1,比原來小則<1。
例如,傳遞一個值 2.0 和 0.5 將會導致繪圖路徑寬度變為原來的兩倍,而高度變為原來的 1/2。

例子:

canvas-scale1.png
function scaleCircle(context){
	context.translate(250,20);
	for (var i = 0; i < 80; i++) {
		context.save(); //儲存上一次畫布的設定
		context.translate(30,30);//每次迭代新的原點都移動到上一次原座標的(30,30)
		context.scale(0.95,0.95);//每次迭代橫縱因子都縮小到原來的0.9倍
		context.rotate(Math.PI/12)//旋轉
		context.beginPath();//開始進行繪製
		// context.fillStyle = "red";
		// context.globalAlpha="0.4";//設定顏色透明度
		context.fillStyle= "rgba(155, 187, 89, 0.7)";
		context.arc(0,0,50,0,Math.PI*2,true);//繪製圓
		context.closePath();
		context.fill();
	}
}

window.onload = function(){
	scaleCircle(context);
}
複製程式碼



11. 矩陣變換

引用了理解網站內容:http://jo2.org/html5-canvas-transform/

canvas上下文物件(context)每次產生一個影像都會建立一個對應的矩陣物件,可直接對canvas變形矩陣作修改,可通過矩陣變換,使圖形產生移動、旋轉、切片、映象反射的效果。

那麼問題來了:圖形都有矩陣,那一個圖形的預設矩陣是什麼樣子的? 答案是:(1,0,0,1,0,0).

①為什麼不全為0?其中:一個圖形在沒有縮放,旋轉,位移什麼的時候,他也會有一個屬性會是1,就是縮放!因為在沒有縮放的情況下,圖形的縮放其實是原大小的1倍.所以,這個預設矩陣裡面才會有兩個1. 所以引數位置1上的1,是表示x軸上的縮放,引數位置4上的1是表示y軸上的縮放!
即:縮放可以根據:context.transform(scaleX,0,0,scaleY,0,0);來設定效果。

舉一反三:
②根據圖形預設矩陣,知矩陣中的最後兩位引數就是表示位移距離的數字(在沒有位移的情況下為0)。 即:移動可以根據:context.transform(scaleX,0,0,scaleY,transX,transY);來設定效果。

③那麼剩下的兩個數字(引數位置2,引數位置3)是不是表示旋轉呢? 不是,他們是表示斜切。什麼是斜切?把一個矩形的任一條邊用力一拉,變成平行四邊形,這就是斜切。 例子:

未進行斜切之前
ctx.arc(200,50,50,0,Math.PI*2) 
.fillRect(200,100,50,50)
.stroke()
複製程式碼
HTML5學習之Canvas基礎知識

斜切之後
ctx.transform(1,Math.tan(Math.PI/180*30),0,1,0,0)//矩形X軸產生了斜切效果
.arc(200,50,w/2,0,Math.PI*2)
.fillRect(200,100,50,50)
.stroke()
複製程式碼
HTML5學習之Canvas基礎知識

程式碼中我們可以看到使用了一個tan函式,如果要用斜切,比如想斜切30度,那麼就必須用tan把30度包起來,x/y軸都是如此.

可以理解為圖形矩陣為ctx.transform(scaleX,skewX,skewY,scaleY,transX,transY);
新座標x' = (scaleX)x + (skewY)y + transX
新座標y' = (skewX)x + (scaleY)y + transY

④那怎麼用transform實現圖形轉換呢?
旋轉的效果要斜切配合縮放實現的。
比如,其他的都不變,只把圖形旋轉30度,程式碼:

var deg = Math.PI/180;
ctx.transform(Math.cos(30*deg),Math.sin(30*deg),-Math.sin(30*deg),Math.cos(30*deg),0,0)
.arc(200,50,50,0,Math.PI*2)
.fillRect(200,100,50,50)
.stroke()
複製程式碼

其中:

cos(30*deg),
sin(30*deg),
-sin(30*deg),
cos(30*deg)
複製程式碼

在使用transform方法實現旋轉時,旋轉N度時,N*deg不變,只要記住對應位置的對應三角函式cos,sin,-sin,cos就行。
原博主傳授的記憶方法:CS-SC=初三-上床,不要忘記負號.

注意:canvas也提供setTransform方法直接把矩陣設為你傳給他的值,會清空前面所有的transform造成的效果。

一個簡單的應用例子:

canvas-transform2.png
context.translate(200,20);
for (var i = 0; i < 80; i++) {
	context.save();
	context.transform(0.95,0,0,0.95,30,30); //縮至0.95倍,移動至(30,30)
	context.rotate(Math.PI/12);
	context.beginPath();
	context.fillStyle = "red";
	context.globalAlpha="0.4";//設定顏色透明度	
	//其中可以利用globalAlpha()為設定透明度,也可以利用fillStyle=rgva()設定透明度
	context.arc(0,0,50,0,Math.PI*2,true);
	// context.closePath();
	context.fill();		
}

context.setTransform(1,0,0,1,10,10); //清空之前的transform樣式,設定新的圖形位置
context.fillStyle = "yellow";
context.fillRect(0,0,50,50);
context.fill();
複製程式碼



12. 圖形組合
預設情況下,兩個圖形有重疊部分時,另外一個圖形會將另一個圖形覆蓋掉。
可以通過globalCompositeOperation屬性設定。

懶得敲之屬性展示系列:

canvas-globalCompositeOperation1.png canvas-globalCompositeOperation2.png canvas-globalCompositeOperation3.png canvas-globalCompositeOperation4.png canvas-globalCompositeOperation5.png
  1. 裁切路徑
    使用clip(),:一旦剪下了某個區域,則所有之後的繪圖都會被限制在被剪下的區域內(不能訪問畫布上的其他區域).
    或者可以理解為,在使用clip()之後,只看到clip之前資訊,而clip之後的只能顯示在clip所選定的區域中的圖形部分,而其他會被隱藏起來。

    參考理解網址:https://www.w3cplus.com/canvas/clip.html

  2. 應用不同的線型
    利用lineWidth、lineGap、lineJoin、miterLimit設定不同線形樣式。
    lineWidth:線粗細,預設為1
    lineGap:線端點樣式,預設為butt,還有round,square可選
    lineJoin:線段連線的樣式,預設為round(圓形),還有bevel(斜角), miter(垂直矩形)可選
    miterLimit:線段相交交叉點的粗細,越大lineJoin所選的樣式越明顯

  3. 繪製線性漸變
    一般用到createLinearGradient()建立線性的漸變物件, 再用addColorStop對線性漸變物件分段新增不同顏色達到漸變效果.

例子:

canvas-linear1.png
// createLinearGradient(漸變起始點x,漸變起始點y,漸變結束點x,漸變結束點y)
var shadow = context.createLinearGradient(0,0,200,0); //也可以理解為漸變的方向的座標
shadow.addColorStop(0,'#ff0000');
shadow.addColorStop(1/7,'#ff9900');
shadow.addColorStop(2/7,'#ffff00');
shadow.addColorStop(3/7,'#00ff00');
shadow.addColorStop(4/7,'#00ffff');
shadow.addColorStop(5/7,'#0000ff');
shadow.addColorStop(6/7,'#ff00ff');
shadow.addColorStop(1,"#ff0000");
context.fillStyle =shadow;
context.strokeStyle = shadow;
context.fillRect(10,10,200,200);
複製程式碼



16. 繪製徑向漸變
利用createRadialGradient()建立徑向漸變物件, 再用addColorStop對徑向漸變物件發射狀新增不同顏色達到徑向漸變效果.
例子:

canvas-radial1.png
// createRadialGradient(漸變開始圓座標x0.漸變開始圓座標y0,開始圓半徑r0,漸變結束圓座標x1,漸變結束圓座標y1,結束圓半徑r1)
var shadow = context.createRadialGradient(85,85,3,100,100,100);
shadow.addColorStop(0,"white");
shadow.addColorStop(1,"orange");
context.fillStyle= shadow;
context.fillRect(10,10,200,200);
複製程式碼



17. 繪製圖案

A.context.createPattern(image,"repeat|repeat-x|repeat-y|no-repeat")
方法在指定的方向內重複指定的元素.
和設定圖片背景有點類似,先new Image()物件,再將img物件傳進createPattern(img,type),再將其賦予給fillStyle,即可達到填充圖片的效果。
例子:

canvas-pic1.png
var img = new Image(); //建立新的Image物件
img.src = "https://www.easyicon.net/api/resizeApi.php?id=1209623&size=128";  //設定影像路徑
img.onload = function(){  //載入影像
    //建立圖案
    var pattern = context.createPattern(img,'repeat');
    context.fillStyle = pattern;
    context.fillRect(0,0,380,190);
	}
複製程式碼

B.drawImage()方法
①在畫布上定點陣圖像:context.drawImage(img,x,y);
②畫布上定點陣圖像,並規定影像的寬度和高度:context.drawImage(img,x,y,width,height);
③剪下影像,並在畫布上定位被剪下的部分:context.drawImage(img,開始剪下座標位置x,開始剪下座標位置y,被剪下影像寬度swidth,被剪下影像高度sheight,圖片位置x,圖片位置y,顯示圖片寬度width,顯示圖片高度height);

var img = new Image(); //建立新的Image物件
img.src = "https://www.easyicon.net/api/resizeApi.php?id=1209623&size=128";  //設定影像路徑
img.onload = function(){  //載入影像
    //建立圖案
    context.drawImage(img,0,0);
    context.font = "nomal 100px SimHei";
    context.shadowOffsetX = 3;
    context.shadowOffsetY = 3;
    context.shadowBlur = 3;
    context.shadowColor = "pink";
    context.createPattern(img,'repeat');
    context.fillStyle = "white";
    context.fillText("wowww",50,165);
	}
複製程式碼



18. 建立陰影

例子:

canvas-shadow2.png
//設定陰影
context.shadowOffsetX =3; //x軸方向的陰影偏移量,負數為向右偏移量
context.shadowOffsetY =3; //y軸方向的陰影偏移量,負數為向上偏移量
context.shadowBlur = 2;//陰影模糊強度
context.shadowColor = "pink";

//繪製矩形
context.fillStyle = "orange";
context.fillRect(20,20,300,80);

//繪製文字
context.font = "nomal 45px SimHei"; //設定文字字型樣式
context.fillStyle = "white"; //設定文字顏色,填充顏色
context.fillText("HTML5/CSS3" ,30,64)//設定文字內容和文字位置
複製程式碼

注意:在設定了陰影后,文字和矩形都會有陰影。

  1. 繪製填充/輪廓文字

A.context.fillText(文字內容,位置x,位置y[,最大寬度]),若文字內容長度超過最大寬度,文字會自動被壓縮

//繪製填充文字
context.font = "nomal 45px SimHei"; //設定文字字型樣式
context.fillStyle = "white"; //設定文字填充顏色
context.fillText("HTML5/CSS3" ,30,64)//設定文字內容和文字位置
複製程式碼

B.context.strokeText(文字內容,位置x,位置y[,最大寬度]),若文字內容長度超過最大寬度,文字會自動被壓縮
//繪製輪廓文字
context.font = "nomal 45px SimHei"; //設定文字字型樣式
context.strokeStyle = "white"; //設定文字輪廓顏色
var text = "HTML5/CSS3";
context.strokeText(text, ,30,64)//設定文字內容和文字位置
複製程式碼

其中:可以通過context.measureText(text)來獲取對應text的寬度.



小白第一次在掘金寫學習總結,從自己理解的角度記錄筆記,可能對某些方法理解存在錯誤,歡迎指出!持續學習更新中....

相關文章