1. 程式人生 > >在瀏覽器上畫圖(canvas的基本用法)

在瀏覽器上畫圖(canvas的基本用法)

        在HTML5裡,我們可以通過canvas標籤來在瀏覽器裡進行畫圖,但是這個標籤並不是能畫圖的,畫圖還是要通過JavaScript,這個標籤只是一個載體。在canvas這個標籤裡,預設的width是300,、height是150,想要設定canvas的長寬的話需要在canvas的width屬性和height屬性那裡設定,這個width和height不屬於style裡的,搞清楚這一點很重要,所以當你在css裡設定width和height的時候,你會發現你不僅把canvas變長或者變寬了,並且連裡面的內容也隨著放大或縮小。如果你在canvas標籤裡面設定了width和height的話,僅僅只是改變長寬,並不影響裡面影象的大小。詳情可見我寫的另一篇文章:

點選開啟連結

        canvas標籤在IE瀏覽器裡只有IE9之前的不支援,那麼對於這些不支援的瀏覽器,我們可以在canvas標籤裡寫入一些內容,當瀏覽器不支援的時候會顯示這些內容,如果瀏覽器支援的話,則會正常渲染canvas而忽略掉這些內容:

<canvas>
    hahaha
</canvas>
<canvas>
    <img src='a.png' />
</canvas>

        在這裡,如果你的瀏覽器支援canvas的話,那麼canvas內的內容則不會顯示,會正常的渲染canvas,如果不支援的話,則會顯示上面的文字或者圖片。

        下面我來講一下canvas比較常用的幾個方法。第一個是getContext()方法。這個方法是用來渲染上下文和它的繪畫功能,在這個方法裡只有一個引數,這個引數值目前來說只有2d,即:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
        在這裡,getContext('2d')通過呼叫CanvasRenderingContext2D介面,來實現下面的一系列的操作。getContext()這個方法在實現canvas畫圖的時候是必不可少的。關於這個引數值2d,它的意思是繪畫2d影象,在未來版本里,可能會多加一個3d的引數值,到時候再詳講3d。

        fillStyle()方法也是用的比較多的,它用於設定或返回用於填充繪畫的顏色、漸變或模式,有三種不同的屬性值,它的定義和用法如何下圖所示:


        最常用的就是給一個矩形填充顏色,如:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.fillStyle = '#FF0000'
cxt.fillRect(20, 20, 150, 100)

        或者是線性漸變,如:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
var grd = cxt.createLinearGradient(0, 0, 175, 50)
grd.addColorStop(0, 'red')
grd.addColorStop(1, 'blue')
cxt.fillStyle = grd
cxt.fillRect(0, 0, 175, 50)

        還有就是填充,如:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
var img = new Image()
img.src = 'file:///d:/桌面/aa.png'
var pat = cxt.createPattern(img, 'repeat')
cxt.rect(0, 0, 400, 400)
cxt.fillStyle = pat
cxt.fill()

        在上面的程式碼中,我們又看見了一些新的方法,如createLinearGradient(x0,y0,x1,y1)方法,這個方法定義的是一個線性漸變,這個方法是作為fillStyle或者strokeStyle的值的。而addColorStop(stop,color)方法則是與createLinearGradient()相對應的方法,它表示的是gradient物件中的顏色和位置。它有兩個引數stop引數介於0.0到1.0之間,表示漸變中開始與結束之間的位置,而color表示結束位置的顏色值。

        createPattern(image,“repeat|repeat-x|repeat-y|no-repeat”)表示的是指定方向上重複的影象,引數值repeat表示在水平和垂直方向上重複影象,如此類推。

        在繼續講下去之前,先提一下帶有fill和stroke字首的方法,帶有fill字首的如fillStyle、fillRect等方法,fill字首表示的是canvas這個畫筆畫出來的影象進行填充,而stroke表示的是canvas這個畫筆畫出來的影象的邊框進行著色,在看canvas的方法的時候你會發現很多帶有fill和stroke字首的方法,理解好這個就不怕記不住了。

        在我們的canvas元素裡,是以畫素為單位的,以左上角為起點。在canvas裡,僅支援原生的圖形繪製:矩形。其他的圖形繪製都至少生成一條路徑。在繪製矩形中,有四種方法:

        1.rect(x,y,width,height)表示的是建立一個矩形,由於沒有填充和對邊框進行著色,這個矩形對人眼來說是不可見的,想讓它可見可以在後面新增stroke()或者fill()方法

        2.fillRect(x,y,width,height)表示的是繪製並填充一個矩形

        3.strokeRect(x,y,width,height)表示的是繪製並給矩形邊框著色

        4.clearRect(x,y,width,height)表示的是清除指定矩形區域,讓清除部分完全透明。

        下面是上面四個方法的綜合例子:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.rect(100,100,600,600)
cxt.stroke()
cxt.fillRect(150,150,500,500)
cxt.clearRect(200,200,400,400)
cxt.strokeRect(250,250,300,300)
cxt.rect(300,300,200,200)
cxt.stroke()
        除了用原生來畫矩形外,我們可以用路徑來畫矩形,設定可以畫多邊形。在這裡,介紹要用到的六種方法:

        1.beginPath()指的是新建一條路徑,生成之後,圖形繪製命令被指向到路徑上生成路徑。

        2.closePath()指的是閉合路徑之後圖形繪製命令又重新指向到上下文中。

        3.stroke()指的是通過線條來繪製圖形輪廓。

        4.fill()指的是通過填充路徑的內容區域生成實心的圖形。

        5.moveTo(x,y)指的是定義開始畫圖時點位置

        6.lineTo(x,y)指的是畫一個點

        在通常情況下,繪製路徑時的第一個點是需要moveTo()方法來宣告的,而後的各條線的路徑通過lineTo()這個方法來繪製其他線,通過closePath()方法來將最後一個點和第一個點來閉合。通過beginPath()來新建或處置一條路徑。在fill()方法中,不管你有沒有用closePath()方法將點線閉合,它都會自動的閉合然後填充,但是strike()方法則不會自動的將點線閉合。下面是運用上面例子來畫了兩個圖形,一個三角形,一個五邊形:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.beginPath()
cxt.moveTo(10,150)
cxt.lineTo(60,150)
cxt.lineTo(60, 280)
//cxt.closePath()
cxt.fill()

cxt.beginPath()
cxt.moveTo(10, 10)
cxt.lineTo(110, 10)
cxt.lineTo(110, 30)
cxt.lineTo(120, 40)
cxt.lineTo(110, 50)
cxt.lineTo(110, 110)
cxt.lineTo(10, 110)
cxt.closePath()
cxt.stroke()

        直來直往的影象比較簡單,下面來講一下彎的圖形如何來弄。曲線有四種方法可以弄,分別是是arc(x,y,radius,startAngle,endAngle,anticlockwise)、arcTo(x1,y1,x2,y2,radius)、quadraticCurveTo(cp1x,cp1y,x,y)和bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)前兩個方法都是圓弧的畫法,紅面兩個是貝賽爾曲線一個是二次一個是三次。圓弧的第一個方法裡的x和y表示的是圓心的座標,startAngle和endAngle表示的開始的角和結束的角,這裡的角度是以弧度為單位,radius表示的是半徑而anticlokwise表示的順時針還是逆時針,true表示逆時針,false表示順時針,false為預設值。另一個圓弧的方法裡x1,y1,x2,y2表示的是起始點的座標和結束點的座標。在二次貝賽爾曲線中,cp1x和cp1y表示的是控制點的座標,x和y表示的是結束點的座標。而在三次貝賽爾曲線裡,cp1x,cp1y,cp2x,cp2y表示的是兩個控制點的座標。

        arc()方法對比arcTo()方法來講,arc方法比較簡單,arcTo()方法的用法比較複雜,並且arcTo()方法只能畫圓弧而不能畫圓,因為arcTo方法利用了前端點、前端點1(x1,y1)、前端點2(x2,y2)這三個點來確定一個夾角,然後通過這個夾角和半徑來繪製一個與夾角相切的圓弧,所以arcTo()方法比較複雜。具體兩個用法如下:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
// ctx.arc(100,70,50,3*Math.PI/2,2*Math.PI)
ctx.lineTo(150,120);
ctx.stroke();
        二次貝賽爾曲線用法如下:
<body>
		<canvas id='a' width='300' height='300'></canvas>
		<button onclick='cha(0)' id='aa'>-x</button>
		<button onclick='cha(1)' id='ab'>+x</button>
		<button onclick='cha(2)' id='ac'>-y</button>
		<button onclick='cha(3)' id='ad'>+y</button>
	</body>
	<script type="text/javascript">
		var a = document.getElementById('a')
		var cxt = a.getContext('2d')
		var x = 20
		var y = 20
		createQC()
		function createQC(){
			cxt.beginPath()
			cxt.moveTo(20, 20)
			cxt.quadraticCurveTo(x,y,200,20)
			cxt.stroke()
			console.log(x+" "+y)
		}
		function clearQC(){
			cxt.clearRect(0,0,300,300)
		}
		function del(eve) {
			if (eve == 2) {
				if (y > 0) {
					y = y - 1
					clearQC()
					createQC()
				}
			}
			else if (eve == 0) {
				if (x > 0) {
					x = x - 1
					clearQC()
					createQC()
				}
			}
			else if (eve ==3) {
				if (y < 300) {
					y = y + 1
					clearQC()
					createQC()
				}
			}
			else if (eve == 1) {
				if (x < 300) {
					x = x + 1
					clearQC()
					createQC()
				}
			}
		}
	</script>

        三次貝塞爾曲線繪製心形:
function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    //三次貝塞爾曲線
    ctx.beginPath();
    ctx.moveTo(75,40);
    ctx.bezierCurveTo(75,37,70,25,50,25);
    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
    ctx.bezierCurveTo(20,80,40,102,75,120);
    ctx.bezierCurveTo(110,102,130,80,130,62.5);
    ctx.bezierCurveTo(130,62.5,130,25,100,25);
    ctx.bezierCurveTo(85,25,75,37,75,40);
    ctx.fill();
  }
}

        在HTML5裡,有一個Path2D物件是專門用來儲存我們弄好的圖形,具體用法如下;
function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 * Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}

        上面講了這些內容都只是繪製圖形而已,到涉及到給圖形著色的時候,需要用到fillStyle和strokeStyle這兩個方法,fillStyle方法是用於給圖形填充顏色,而strokeStyle方法是用於給圖形的輪廓著色。一旦給strokeStyle或者fillStyle賦值了,那麼後面新繪製的影象的顏色就是strokeStyle或者fillStyle設定的值,如果想要給每個圖形賦予不同的顏色,則需要重新設定fillSty或者strokeStyle了。

        透明的話可以運用globalAlpha方法或者利用css3的rgba屬性。globalAlpha的取值範圍是0.0到1.0。運用這兩個和上面的fillStyle方法程式碼如下:

function draw(){
			var ctx = document.getElementById('myCanvas').getContext('2d')
			ctx.fillStyle = 'red'
			ctx.fillRect(40,40,200,200)
			ctx.fillStyle = 'white'
			
			// ctx.globalAlpha = 0.3
			// ctx.beginPath()
			// ctx.arc(140, 140, 100, 0, Math.PI * 2, true)
			// ctx.fill()

			ctx.beginPath()
			ctx.fillStyle = 'rgba(255,255,255,0.3)'
			ctx.arc(140, 140, 90, 0, Math.PI * 2, true)
			ctx.fill()
		}
draw()

        在畫布裡,可以通過fillText(text,x,y[,maxWidth])和strokeText(text,x,y[,maxWidth])方法來繪製文字。在這些方法裡,text表示的是文字,x和y指定開始的位置,maxWidth指的是繪製的最大寬度。詳細程式碼如下所示:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.font = '30px serif'

cxt.fillStyle = 'green'
cxt.fillText('Hello world', 10, 30)

cxt.strokeStyle = 'red'
cxt.strokeText('Hello world', 10, 60) 

        我們除了可以繪製點線面和填充顏色和新增文字外,也可以新增圖片。在canvas裡,新增圖片有三種不同的方法:第一種就是最普通的方法drawImage(image,x,y),這裡的x和y表示目標的起始座標,image表示新增哪副影象。第二種方法是drawImage(image,x,y,width,height),這種用於對圖片的縮放,width和height指的是圖片的大小。第三種方法是drawImage(image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight),這種方法用於切片,該方法前五個引數和第二種方法的引數一樣,後面四個引數表示的是定義切片的目標明顯位置和大小。下面的例子就是第二種和第三種的用法:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
		
var img1 = new Image()
// img1.src = 'file:///d:/桌面/pig.jpg'
img1.src = 'http://pic.wenwo.com/fimg/513558924_550.jpg'

var img2 = new Image()
// img2.src = 'file:///d:/桌面/timg.jpg'
img2.src = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1507269085638&di=f71daf5be98181991d2f056c103e91df&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimage%2Fc0%253Dshijue1%252C0%252C0%252C294%252C40%2Fsign%3D587a7e3ede0735fa85fd46faf63865c6%2Fe7cd7b899e510fb3fc46abb2d333c895d1430cbc.jpg'

cxt.drawImage(img2,0,0,300,270)
cxt.drawImage(img1,200,30,200,180,50,40,200,180)

        在canvas裡,canvas的狀態就是當前畫面應用的所有樣式和變形的一個快照,這個狀態儲存在棧中。在canvas裡,有save()和restore()方法,save()就是把當前的狀態壓到棧中儲存,而restore()就是把上一次狀態從棧中彈出,所有設定都恢復。下面這個例子就是對save()和restore()的綜合應用:
var c = document.getElementById('myCanvas')
var ctx = c.getContext('2d')
		
ctx.fillRect(0,0,150,150);   // 使用預設設定繪製一個矩形
ctx.save();                  // 儲存預設狀態

ctx.fillStyle = '#09F'       // 在原有配置基礎上對顏色做改變
ctx.fillRect(15,15,120,120); // 使用新的設定繪製一個矩形
ctx.save();                  // 儲存當前狀態
  		
ctx.fillStyle = '#FFF'       // 再次改變顏色配置
ctx.globalAlpha = 0.5;    
ctx.fillRect(30,30,90,90);   // 使用新的配置繪製一個矩形

ctx.restore();               // 重新載入之前的顏色狀態
ctx.fillRect(45,45,60,60);   // 使用上一次的配置繪製一個矩形

ctx.restore();               // 載入預設顏色配置
ctx.fillRect(60,60,30,30);   // 使用載入的配置繪製一個矩形

        在canvas裡,我們可以運用translate(x,y)來對圖形進行位移 ,這個方法和我們剛剛上面所講的drawImage(image,x,y)有什麼區別呢?其實你一看到drawImage()這個方法的名字,你就知道這個方法是用來繪製圖片的,雖然也能進行位移,但是我們的translate()方法是不能繪製圖片的,translate()方法的意思是重新對映畫布上的(0,0)位置。所以,你用這個方法的時候,他的x和y的偏移量每次都是以原點為起始點進行位移的,如:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d') 

cxt.fillRect(10,10,50,50)
cxt.translate(10,70)
cxt.fillRect(10,10,50,50)
        有平移肯定少不了旋轉和縮放的啦。旋轉的方法是rotate(angle),這個angle指的是弧度,這個方法是以canvas的原點為圓心順時針旋轉的,如果要改變圓心則通過translate()方法來進行改變。下面的例子註釋的是用translate()方法,沒用註釋的沒用了translate()方法。
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
		
cxt.fillRect(100,100,100,100)
cxt.fillStyle = 'red'
cxt.rotate(Math.PI/4)
cxt.fillRect(100,100,100,100)

// cxt.fillRect(100,100,100,100)
// cxt.fillStyle = 'red'
// cxt.translate(150,80)  //這個原點很難找,知道公式的小夥伴可以把它貼到評論那裡,樓主感謝了
// cxt.rotate(Math.PI/4)
// cxt.fillRect(0,0,100,100)
        縮放的方法是scale(x,y),這兩個引數表示橫軸和縱軸的縮放因子,這兩個引數都必須為正值,大於1.0表示放大,小於1.0表示縮小,等於1.0則無變化。如果對繪圖進行縮放的話,那麼所有之後的繪圖也會被縮放,定位也會被縮放,所以要注意。例子:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
        最後,可以通過transform(m11,m12,m21,m22,dx,dy)這個方法來把上面的平移、旋轉和縮放給整合起來用,transform()方法的原理是涉及到計算機圖形學的二維圖形的幾何變換矩陣,深入理解的話涉及到數學的問題,感興趣的話可以查閱這個文章:二維圖形的幾何變換,這裡不做深究,引數的詳細用法如下所示:

        transform()方法和setTransform()方法的主要區別是每次用transform()方法都是用上一次的圖形來進行的操作的,而每次運用setTransform()方法的話他每次都會用最初的圖形來進行操作的,並且不管執行多少次setTransform()方法,最後面都只會出現一個圖形,如:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");

ctx.fillStyle="yellow";
ctx.fillRect(0,0,250,100)

ctx.transform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="red";
ctx.fillRect(0,0,250,100);

ctx.transform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="blue";
ctx.fillRect(0,0,250,100);
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");

ctx.fillStyle="yellow";
ctx.fillRect(0,0,250,100)

ctx.setTransform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="red";
ctx.fillRect(0,0,250,100);

ctx.setTransform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="blue";
ctx.fillRect(0,0,250,100);


        最後介紹一個方法clip(),這個方法表示的是裁剪,是從原始畫布中剪下任意形狀和尺寸,一旦裁剪了某個區域,則所有之後的繪圖都會被限制在被裁剪的區域內。如:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.rect(50,20,200,120)
cxt.stroke()
cxt.clip()
cxt.fillStyle = 'green'
cxt.fillRect(0,0,150,100)

        當然,上面的只是canvas的基礎入門,canvas可以說是入門容易精通難,常見用的比較多的有購物網站那裡把滑鼠放到商品圖片的時候會放大、一些動畫效果比如時鐘,還可以用到遊戲上。詳細的canvas教程推薦去MDN檢視,因為是Mozilla大公司寫的,比較權威。