Canvas畫圖之中級篇
前段時間介紹了canvas畫圖的初級篇,今天接着介紹中級篇了...
一.繪制形狀
繪制圖形不僅僅是利用線條來實現繪圖, 還可以有快捷的繪制圖形的辦法
1.繪制矩形
2.繪制圓弧
1 繪制矩形
繪制矩形的方法
CanvasRenderingContext2D.strokeRect
CanvasRenderingContext2D.fillRect
CanvasRenderingContext2D.rect
注意: rect 方法就是矩形路徑, 還需要使用 fill 或 stroke 才可以看到效果. 因此一般使用 strokeRect 或 fillRect 直接可以看到結果.
清除矩形區域
CanvasRenderingContext2D.clearRect
1.1繪制矩形框
語法: CanvasRenderingContext2D.strokeRect( x, y, width. height )
描述:
用來繪制一個矩形. 比起直接使用 moveTo 和 lineTo 方法要簡單許多.
該方法的前兩個參數表示繪制矩形的左上角的坐標. 后兩個參數表示這個矩形的寬高.
使用該方法不需要使用 moveTo 方法設置起始點, 也不需要調用 stroke 等繪畫方法.
繪制的矩形支持 strokeStyle 設置顏色樣式.
代碼:
...
ctx.strokeStyle = 'blue';
ctx.strokeRect( 100, 100, 200, 100 );
效果
1.2 繪制填充矩形
語法: CanvasRenderingContext2D.fillRect( x, y, width. height )
描述:
用來繪制一個矩形. 比起直接使用 moveTo 和 lineTo 方法要簡單許多.
該方法的前兩個參數表示繪制矩形的左上角的坐標. 后兩個參數表示這個矩形的寬高.
使用該方法不需要使用 moveTo 方法設置起始點, 也不需要調用 stroke 等繪畫方法.
繪制的矩形支持 fillStyle 設置顏色樣式.
案例
...
ctx.fillStyle = 'pink';
ctx.fillRect( 100, 100, 200, 100 );
效果
1.3清除矩形區域
語法: CanvasRenderingContext2D.clearRect( x, y, width, height )
描述:
用於清除畫布中的矩形區域的內容.
參數 x, y 表示矩形區域左上角的坐標, width 與 height 表示矩形區域的寬高.
案例
...
ctx.fillRect( 100, 100, 150, 100 );
ctx.clearRect( 120, 120, 60, 60 );
注意:要清除矩形內部的區域時,clearRect的左上角坐標的x,y都要略大於矩形左上角坐標x,y值,否則清除的不完整。
效果
應用:可以利用繪制圖形與清除矩形區域實現簡單的動畫
代碼:
...
var x = 10, y = 10, oldx = 10, oldy = 10;
var width = 100, height = 50;
var timerId = setInterval(function () {
ctx.clearRect( oldx - 1, oldy - 1, width + 2, height + 2 );
ctx.strokeRect( x, y, width, height );
oldx = x;
oldy = y;
x += 4;
y += 2;
if ( oldy >= 200 ) {
// clearInterval( timerId );
x = 10, y = 10;
}
}, 20);
有時為了簡單常常將整個畫布都清除, 這樣就不用每次計算清除的問題.
ctx.clearRect( 0, 0, cas.width, cas.height );
// 也可以設置畫布寬度, 這樣就會自動清除
cas.width = cas.width;
2.繪制圓弧
繪制圓弧的方法有:
CanvasRenderingContext2D.arc()
CanvasRenderingContext2D.arcTo()
2.1繪制圓弧
語法: CanvasRenderingContext2D.arc( x, y, radius. startAngle. endAngle, anticlockwise )
描述:
a.該方法用於繪制一段弧, 配合開始點的位置 與 stroke 方法或 fill 方法可以繪制扇形.
b.方法中的前兩個參數 x, y 表示繪制圓弧的圓心坐標.
c.參數 radius 表示圓弧半徑, 單位為弧度.
d.參數 startAngle 與 endAngle 表示開始到結束的角度. 角度以水平向右為 0 弧度, 順時針為正方向.
e.參數 anticlockwise 表示是否采用默認的正向角度, 如果傳入 true 表示逆指針為正. 該參數可選.
代碼:
// 在 200, 200 的地方繪制一段半徑為 100 的圓弧, 圓心角為 - PI / 2 到 PI / 4
...
ctx.arc( 200, 200, 100, -Math.PI/2, Math.PI/4 );
ctx.stroke();
// 為了方便看清楚結構, 繪制坐標軸
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo( 50, 200 );
ctx.lineTo( 350, 200 );
ctx.moveTo( 200, 50 );
ctx.lineTo( 200, 350 );
ctx.moveTo( 200, 200 );
ctx.lineTo( 300, 300 );
ctx.stroke();
效果為:
2.1.1. 注意事項
使用 arc 繪圖的時候, 如果沒有設置 moveTo 那么會從開始的繪弧的地方作為起始點. 如果設置了 moveTo, 那么會連線該點與圓弧的起點.
如果使用 stroke 方法, 那么會從開始連線到圓弧的起始位置. 如果是 fill 方法, 會自動閉合路徑填充.
例如
2.2繪制扇形動畫
繪制扇形動畫, 就是每隔幾毫秒( 20 毫秒)擦除以前繪制的內容, 然后在以前繪制的基礎上比以前多繪制一點東西. 這里多繪制的內容就是由角度決定. 比如一開始角度從 -Math.PI / 2 開始繪制. 那么每次角度都 +0.1, 直到 繪制到 Math.PI * 3 / 2 為止.
...
ctx.fillStyle = 'green';
var startAngle = -Math.PI / 2,
angle = startAngle,
x = 200, y = 200,
r = 100;
var intervalId = setInterval(function () {
// 清除之前繪制的內容
ctx.clearRect( 0, 0, cas.width, cas.height );
// 角度增量
angle += 0.1;
// 判斷是否停止計時器
if ( angle >= Math.PI * 3 / 2 ) {
clearInterval( intervalId);
angle = Math.PI * 3 / 2;
console.log( '繪制完成' );
}
// 繪制
ctx.moveTo( x, y );
ctx.arc( x, y, r, startAngle, angle );
ctx.fill();
}, 20);
2.3 繪制餅形圖
繪制餅形圖最大的特點是角度是疊加的. 開始從 -Math.PI/2 開始繪制, 達到執行角 x 后, 下一個區域從 x 開始繪制, 然后有到一個角 y 停下來. 如此反復到 Math.PI * 3 / 2 結束.
2.3.1. 三等分餅形圖
繪制一個三等分的餅形圖, 顏色使用 紅, 綠, 藍.
var x = 200, y = 200,
r = 100,
step = Math.PI * 2 / 3, // 120 度一個區域
start = -Math.PI / 2, // 起始角度
colors = [ 'red', 'green', 'blue' ];
for ( var i = 0; i < 3; i++ ) {
ctx.beginPath();
ctx.moveTo( x, y );
ctx.fillStyle = colors[ i ];
ctx.arc( x, y, r, start, start+=step );
ctx.fill();
}
效果:
2.3.2 根據數據定義角度
根據數據源定義角度, 就是將所有的數據求和, 按照總和為 2 * Math.PI 的結論計算出每一個數據部分的弧度值. 同時顏色可以提前定義好.
var data = [ 456, 150, 321, 666 ];
//1.應該求得整個數組的和
var sum=0;
data.forEach(function (v) {
sum+=v;
})
//計算每個數字對應的比例 rdata表示對應的弧度數組
var rdata=data.map(function (v) {
//弧度/2PI=數字/數組數字和 故弧度=數字/數組數字和*2PI
return v*2*Math.PI/sum;
})
//准備顏色
//開始循環繪制
var startAngle=radian(-90);//餅形圖一般默認是從-90度開始繪制的
var x=cas.width/2;
var y=cas.height/2;
var r=100;
rdata.forEach(function (v,i) {
ctx.beginPath();
ctx.moveTo(x,y);
ctx.fillStyle=colors[(i+1)*11];//為了使各部分選擇的顏色不那么相近
ctx.arc(x,y,r,startAngle,startAngle+=v);
ctx.fill();
})
效果為:
2.4. 繪制相切弧
語法: CanvasRenderingContext2D.arcTo( x1, y1, x2, y2, radius )
描述:
該方法用於繪制圓弧
繪制的規則是當前位置與第一個參考點連線, 繪制的弧與該直線相切.
同時連接兩個參考點, 圓弧根據半徑與該連線相切
例如有一個起始點 ( 100, 100 ), 那么繪制其點. 顏色設置為紅色.
ctx.fillStyle = 'red';
ctx.fillRect( 100 - 4, 100 - 4, 8, 8 );
然后兩個參考點分別為 ( 100, 300 ) 和 ( 300, 300 ), 繪制出該點
ctx.fillRect( 100 - 4, 300 - 4, 8, 8 );
ctx.fillRect( 300 - 4, 300 - 4, 8, 8 );
連接兩個參考點
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo( 100, 300 );
ctx.lineTo( 300, 300 );
ctx.stroke();
調用 arcTo 方法繪制圓弧. 記得將起始點設置為 ( 100, 100 )
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo( 100, 100 );
ctx.arcTo( 100, 300, 300, 300, 100 );
ctx.stroke();
效果為:
提示: 使用該方法可以使用圓弧連接兩條直線, 而不用計算復雜的起始角度與結束角度. 因此多用於繪制圓角矩形。
3.繪制文本
繪制文本的方法
CanvasRenderingContext2D.fillText()
CanvasRenderingContext2D.strokeText()
CanvasRenderingContext2D.measureText()
文本樣式
CanvasRenderingContext2D.font
CanvasRenderingContext2D.textAlign
CanvasRenderingContext2D.textBaseline
3.1. 繪制文字
語法:
CanvasRenderingContext2D.strokeText( text, x, y[, maxWidth] )
CanvasRenderingContext2D.fillText( text, x, y[, maxWidth] )
描述:
這兩個方法的功能都是在給定的 x, y 坐標下繪制文本內容.
stroke 繪制描邊文字, 文字內空心. fill 繪制填充文字, 即實心文字.
最后一個參數可選, 用於限制文字的總寬. 特殊條件下, 系統會自動調整文字寬度與大小以適應該參數限制.
3.2計算文本尺寸
語法: CanvasRenderingContext2D.measureText()
描述:
該方法返回一個文本尺寸對象, TextMetrics 對象.
TextMetrics 對象屬性很多, 常用的 width 屬性可以獲取文字的寬度.
3.3. 設置文字屬性
3.3.1. 設置文字字體
語法: CanvasRenderingContext2D.font = value
描述:
該屬性用於設置繪制字體的各種信息, 與 CSS 語法一致, 設置字體形狀, 樣式, 字號粗細等.
其順序可以是: style | variant | weight | size/line-height | family.
默認值為 10px sans-serif
修改字號后查看 strokeText 與 fillText 的區別
...
ctx.font = '50px 黑體';
ctx.strokeRect( 100, 100, 200, 50 );
ctx.strokeText( 'Hello JK', 100, 100 );
ctx.strokeRect( 100, 200, 200, 50 );
ctx.fillText( 'Hello JK', 100, 200 );
效果為:
3.3.2. 設置字體水平對齊方式
語法: CanvasRenderingContext2D.textAlign = value
描述:
該屬性用於設置文字的水平對齊方式. 設置文字居中, 靠左右對齊等.
該屬性可以設置的值有: start( 默認 ), end, left, right, center.
start 表示根據參考基准點的垂直直線左靠對齊
end 表示根據參考基准點的垂直直線右靠對齊
left 與 right 就是左對齊與右對齊的意思
center 就是居中的意思.
3.3.3. 設置字體垂直對齊方式
語法: CanvasRenderingContext2D.textBaseline = value
描述:
該方法設置文字在垂直方向上的對齊方式.
該屬性可以取值: top, middle, bottom, hanging, alphabetic, ideographic
基於參考點的直線, 其中 top, middle, buttom 分別表示靠上, 居中, 靠下對齊.
alphabetic 表示字母基線, 類似於英文字母的對齊方式. 例如 a, g, f 等字母.
ideographic 表意對齊. 使用字母對齊中超出的字母為參考. 即比字母基線略靠下.
所有的對齊方式是根據文字特點相關的. 對於中文主要使用的還是 top, bottom 和 middle.
top, middle, bottom 使用的較多
alphabetic 表示字母參考線, ideographic 會比它低一點, hanging 表示懸掛.
4.繪制圖片
繪制圖像雖然只有一個 drawImage 函數, 但是該函數有多重參數形式.
CanvasRenderingContext2D.drawImage( img, dx, dy ).(三參數)
CanvasRenderingContext2D.drawImage( img, dx, dy, dWidth, dHeight ).(五參數)
CanvasRenderingContext2D.drawImage( img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight ).(九參數)
4.1. 繪制簡單圖像
語法: CanvasRenderingContext2D.drawImage( img, dx, dy )
描述:
使用三個參數, 允許在畫布上的任意位置繪制圖片.
參數 img 是指圖片對象. 可以是 img 標簽, 或者是 video 標簽, 已經另一個 canvas 等.
提示:需要注意的是如果直接添加 img 對象是不可以的, 需要等待其加載,因為加載圖片需要時間, 因此將繪制的方法放在 onload 事件中.
這里使用 new Image() 與 document.createElement( 'img' ) 是一樣的.
4.2. 在指定區域內繪制圖像
當圖片比較大的時候, 如果使用這樣的方式繪圖, 那么圖片可能會完全覆蓋畫布. 例如:
因此, 需要將其控制在一個矩形區域內繪制.
語法: CanvasRenderingContext2D.drawImage( img, dx, dy, dWidth, dHeight )
描述:
參數 dWidth, dHeight 表示繪制的矩形區域. 繪制的 dx, dy 坐標決定了開始.
該方法繪制的圖像會在指定范圍內被壓縮顯示.
將上面的圖繪制在 100 * 100 的范圍內.
...
ctx.drawImage( img, 100, 100, 100, 100 );
效果
如果希望正常顯示, 需要固定一個寬度或者高度, 然后根據比例計算出另一個值. 這里假定高度是 100. 計算寬度后繪圖.
...
var heigth = 100, width;
img.onload = function () {
width = img.width * height / img.height;
ctx.drawImage( img, 100, 100, width, height );
};
效果
4.3. 繪制任意圖像
類似於 CSS 中處理按鈕等小圖標的技巧, 將很多的效果圖集中在一張 png 格式的背景透明的圖片中, 這樣可以提高效率也便於維護. 那么 drawImage 同樣支持該方式繪圖. 在畫布中的指定位子與指定區域內, 繪制圖片中的某個矩形區.
語法: CanvasRenderingContext2D.drawImage( img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight )
描述: 這里的帶有 s 前綴的參數就是指圖源的矩形區域.
例如將圖片的 ( 100, 100, 300, 200 ) 處的內容繪制到頁面的 ( 100, 100, 300, 200 ) 的位置.
'''
ctx.drawImage( img, 100, 100, 300, 200, 100, 100, 300, 200 );
效果
4.4. 繪制動畫
有了圖片部分繪制的功能, 我們就可以嘗試繪制動畫了. 准備一張圖片
然后開始的時候繪制第一行的第一張圖. 計算它的坐標與寬高
var img = new Image();
img.src = './imgs士兵(1).png'
img.onload = function () {
var width = img.width / 4;
var height = img.heigth / 4;
// 第一張圖的頂點是 0, 0, 寬高是 width, height
};
那么第一行的第二張圖就是 ( 0, width, width, height ), 第三張就是 ( 0, width * 2, width, height ). 因此第一行的第 i 張圖就是
var x = 0;
var y = width * ( i - 1 );
同理得到, 第 j 列的 x 坐標是 height * ( j - 1 )
現在繪制第一張圖
...
var img = new Image();
img.src = './imgs/士兵(1).png'
img.onload = function () {
var width = img.width / 4;
var height = img.height / 4;
ctx.drawImage( img, 0, 0, width, height, 100, 100, width, height );
};
現在要求, 每隔 200 毫秒就切換一張圖片, 現在只考慮第一行的圖片.
...
var img = new Image();
img.src = './imgs/士兵(1).png'
img.onload = function () {
var width = img.width / 4;
var height = img.height / 4;
var i = 0;
setInterval(function () {
ctx.clearRect( 0, 0, cas.width, cas.height );
ctx.drawImage( img, width * (i++ % 4), 0, width, height, 100, 100, width, height );
}, 200 );
};