Canvas里,所有圖形都繪制在幀上,繪制方法不會(huì)將繪制好的圖形元素作為一個(gè)返回值輸出,js也無法獲取到已經(jīng)繪制好的圖形元素,在Canvas中繪制的圖形都是一個(gè)整體。
假設(shè)用Canvas繪制了一個(gè)圖形,要判斷一個(gè)事件是否發(fā)生在該圖形上,有個(gè)isPointInPath方法,但是該方法僅判斷當(dāng)前上下文環(huán)境中的路徑,所以當(dāng)Canvas里已經(jīng)繪制了多個(gè)圖形時(shí),僅能以最后一個(gè)圖形的上下文環(huán)境來判斷事件。這種問題的解決方法是:當(dāng)事件發(fā)生時(shí),重繪所有圖形,每繪制一個(gè)就使用isPointInPath方法,判斷事件坐標(biāo)是否在該圖形覆蓋范圍內(nèi)。當(dāng)圖形過多時(shí),為了在性能和視覺效果上達(dá)到更好的效果,我們可以采取一種數(shù)學(xué)方法。
多個(gè)矩形框
如圖,紅色部分是由很多個(gè)矩形框拼接而成的平滑區(qū)域。需求是當(dāng)鼠標(biāo)移至矩形框上時(shí)出現(xiàn)文本提示框來顯示當(dāng)前是第幾個(gè)矩形。下圖是這些矩形框的坐標(biāo):
坐標(biāo)
接下來繪制矩形框:
繪制
判斷一個(gè)點(diǎn)是否在矩形框內(nèi)的數(shù)學(xué)方法為:假設(shè)點(diǎn)為o,矩形為abcd,如果Soab+Sobc+Socd+Soda=Sabcd,則該點(diǎn)在矩形框內(nèi),否則不在。
判斷
涉及到的函數(shù)有:計(jì)算兩點(diǎn)之間距離、計(jì)算三角形面積、判斷點(diǎn)是否在矩形內(nèi)。當(dāng)鼠標(biāo)移動(dòng)的時(shí)候,循環(huán)判斷鼠標(biāo)坐標(biāo)點(diǎn)是否在矩形框內(nèi),在的話就出現(xiàn)提示框。為了減少判斷、提高性能,創(chuàng)建一個(gè)函數(shù)用于計(jì)算矩形框的水平跨度,即X軸范圍,當(dāng)目標(biāo)點(diǎn)不在該范圍內(nèi)就不做是否在矩形框內(nèi)的判斷。
事件處理程序
其中minmax是一個(gè)二維數(shù)組,保存著所有矩形框的X軸跨度。
當(dāng)條件成立時(shí),效果如下,即顯示文本:
效果
家好! 歡迎來到本教程,我們將深入了解使用 HTML 畫布和 JavaScript 在代碼中創(chuàng)建有趣的氣泡的世界。 最好的部分? 我們將只使用一點(diǎn) HTML 和所有 JavaScript,而不是 CSS 來實(shí)現(xiàn)所有這一切。
今天,我們要掌握以下幾個(gè)概念:
使用畫布上下文的 arc 方法創(chuàng)建圓。
利用 requestAnimationFrame 函數(shù)實(shí)現(xiàn)平滑的圓形動(dòng)畫。
利用 JavaScript 類的強(qiáng)大功能來創(chuàng)建多個(gè)圓圈,而無需重復(fù)代碼。
向我們的圓圈添加描邊樣式和填充樣式以獲得 3D 氣泡效果。
你可以跟著我一起看,或者如果你想看源代碼,可以使用最終的codepen
首先,我們需要一個(gè) HTML5 Canvas 元素。 Canvas 是創(chuàng)建形狀、圖像和圖形的強(qiáng)大元素。 這就是氣泡將產(chǎn)生的地方。 讓我們來設(shè)置一下 -
<canvas id="canvas"></canvas>
為了使用畫布做任何有意義的事情,我們需要訪問它的上下文。 Context 提供了在畫布上渲染對(duì)象和繪制形狀的接口。
讓我們?cè)L問畫布及其上下文。
const canvas=document.getElementById('canvas');
const context=canvas.getContext('2d');
我們將設(shè)置畫布以使用整個(gè)窗口的高度和寬度 -
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
讓我們通過添加一些 css 為畫布提供一個(gè)漂亮的舒緩淺藍(lán)色背景。 這是我們要使用的唯一 CSS。 如果您愿意,也可以使用 JavaScript 來完成此操作。
#canvas {
background: #00b4ff;
}
讓我們進(jìn)入有趣的部分。 我們將通過單擊畫布來創(chuàng)建氣泡。 為了實(shí)現(xiàn)這一點(diǎn),我們首先創(chuàng)建一個(gè)點(diǎn)擊事件處理程序:
canvas.addEventListener('click', handleDrawCircle);
由于我們需要知道在畫布上單擊的位置,因此我們將在句柄 DrawCircle 函數(shù)中跟蹤它并使用事件的坐標(biāo) -
//We are adding x and y here because we will need it later.
let x, y
const handleDrawCircle=(event)=> {
x=event.pageX;
y=event.pageY;
// Draw a bubble!
drawCircle(x, y);
};
為了創(chuàng)建圓圈,我們將利用畫布上下文中可用的 arc 方法。 Arc 方法接受 x 和 y - 圓心、半徑、起始角和結(jié)束角,對(duì)于我們來說,這將是 0 和 2* Math.PI,因?yàn)槲覀冋趧?chuàng)建一個(gè)完整的圓。
const drawCircle=(x, y)=> {
context.beginPath();
context.arc(x, y, 50, 0, 2 * Math.PI);
context.strokeStyle='white';
context.stroke();
};
現(xiàn)在我們有了圓圈,讓我們讓它們移動(dòng),因?yàn)椤?/p>
GIF
請(qǐng)記住,當(dāng)我們創(chuàng)建圓時(shí),我們使用了 arc 方法,它接受 x 和 y 坐標(biāo) - 圓的中心。 如果我們快速移動(dòng)圓的 x 和 y 坐標(biāo),就會(huì)給人一種圓在移動(dòng)的印象。 讓我們?cè)囋嚢桑?/span>
//Define a speed by which to increment to the x and y coordinates
const dx=Math.random() * 3;
const dy=Math.random() * 7;//Increment the center of the circle with this speed
x=x + dx;
y=y - dy;
我們可以將其移至函數(shù)內(nèi) -
let x, y;
const move=()=> {
const dx=Math.random() * 3;
const dy=Math.random() * 7; x=x + dx;
y=y - dy;
};
為了讓我們的圓圈無縫移動(dòng),我們將創(chuàng)建一個(gè)動(dòng)畫函數(shù)并使用瀏覽器的 requestAnimationFrame 方法來創(chuàng)建一個(gè)移動(dòng)的圓圈。
const animate=()=> {
context.clearRect(0, 0, canvas.width, canvas.height);
move();
drawCircle(x,y); requestAnimationFrame(animate);
};//Don't forget to call animate at the bottom
animate();
現(xiàn)在我們已經(jīng)創(chuàng)建了一個(gè)圓圈,是時(shí)候創(chuàng)建多個(gè)圓圈了!
但在我們創(chuàng)建多個(gè)圓圈之前,讓我們準(zhǔn)備一下我們的代碼。為了避免重復(fù)我們的代碼,我們將使用類并引入 Particle 類。 粒子是我們動(dòng)態(tài)藝術(shù)作品和動(dòng)畫的構(gòu)建塊。 每個(gè)氣泡都是一個(gè)粒子,具有自己的位置、大小、運(yùn)動(dòng)和顏色屬性。 讓我們定義一個(gè) Particle 類來封裝這些屬性:
class Particle {
constructor(x=0, y=0) {}
draw() {
// Drawing the particle as a colored circle
// ...
} move() {
// Implementing particle movement
// ...
}
}
讓我們將一些已設(shè)置的常量移至 Particle 類 -
class Particle {
constructor(x=0, y=0) {
this.x=x;
this.y=y;
this.radius=Math.random() * 50;
this.dx=Math.random() * 3;
this.dy=Math.random() * 7;
}
draw() {
// Drawing the particle as a colored circle
// ...
} move() {
// Implementing particle movement
// ...
}
}
draw 方法將負(fù)責(zé)在畫布上渲染粒子。 我們已經(jīng)在drawCircle中實(shí)現(xiàn)了這個(gè)功能,所以讓我們將它移動(dòng)到我們的類中并將變量更新為類變量
class Particle {
constructor(x=0, y=0) {
this.x=x;
this.y=y;
this.radius=Math.random() * 50;
this.dx=Math.random() * 3;
this.dy=Math.random() * 7;
this.color='white';
}
draw() {
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
context.strokeStyle=this.color;
context.stroke(); context.fillStyle=this.color;
context.fill();
} move() {}
}
同樣,讓我們?cè)陬愔幸苿?dòng) move 函數(shù) -
move() {
this.x=this.x + this.dx;
this.y=this.y - this.dy;
}
現(xiàn)在,我們需要確保在事件處理程序中調(diào)用 Particle 類。
const handleDrawCircle=(event)=> {
const x=event.pageX;
const y=event.pageY;
const particle=new Particle(x, y);
};canvas.addEventListener('click', handleDrawCircle);
由于我們需要在 animate 函數(shù)中訪問該粒子,以便調(diào)用其 move 方法,因此我們將該粒子存儲(chǔ)在一個(gè)名為 molecularArray 的數(shù)組中。 當(dāng)創(chuàng)建大量粒子時(shí),這個(gè)數(shù)組也會(huì)很有幫助。 這是反映這一點(diǎn)的更新代碼 -
const particleArray=[];
const handleDrawCircle=(event)=> {
const x=event.pageX;
const y=event.pageY; const particle=new Particle(x, y);
particleArray.push(particle);
};canvas.addEventListener('click', handleDrawCircle);
記得也要更新動(dòng)畫功能 -
此時(shí),您將在屏幕上看到這個(gè)粒子 -
驚人的! 現(xiàn)在,到了有趣的部分! 讓我們創(chuàng)建很多圓圈并設(shè)計(jì)它們的樣式,使它們看起來像氣泡。
為了創(chuàng)建大量氣泡,我們將使用 for 循環(huán)創(chuàng)建粒子并將它們添加到我們?cè)诖颂巹?chuàng)建的粒子數(shù)組中。
const handleDrawCircle=(event)=> {
const x=event.pageX;
const y=event.pageY;
for (let i=0; i < 50; i++) {
const particle=new Particle(x, y);
particleArray.push(particle);
}
};canvas.addEventListener('click', handleDrawCircle);
在動(dòng)畫函數(shù)中,我們將通過清除畫布并在新位置重新繪制粒子來不斷更新畫布。 這會(huì)給人一種圓圈在移動(dòng)的錯(cuò)覺。
const animate=()=> {
context.clearRect(0, 0, canvas.width, canvas.height);
particleArray.forEach((particle)=> {
particle?.move();
particle?.draw();
}); requestAnimationFrame(animate);
};animate();
現(xiàn)在我們有了移動(dòng)的氣泡,是時(shí)候給它們添加顏色,使它們看起來像氣泡了!
我們將通過向氣泡添加漸變填充來實(shí)現(xiàn)此目的。 這可以使用 context.createRadialGradient 方法來完成。
const gradient=context.createRadialGradient(
this.x,
this.y,
1,
this.x + 0.5,
this.y + 0.5,
this.radius
);
gradient.addColorStop(0.3, 'rgba(255, 255, 255, 0.3)');
gradient.addColorStop(0.95, '#e7feff');context.fillStyle=gradient;
恭喜! 您剛剛僅使用 HTML Canvas 和 JavaScript 創(chuàng)建了一些超級(jí)有趣的東西。 您已經(jīng)學(xué)習(xí)了如何使用 arc 方法、利用 requestAnimationFrame、利用 JavaScript 類的強(qiáng)大功能以及使用漸變?cè)O(shè)計(jì)氣泡以實(shí)現(xiàn) 3D 氣泡效果。
請(qǐng)隨意嘗試顏色、速度和大小,使您的動(dòng)畫真正獨(dú)一無二。
請(qǐng)隨意嘗試顏色、速度和大小,使您的動(dòng)畫真正獨(dú)一無二。
我希望您在學(xué)習(xí)本教程時(shí)能像我在創(chuàng)建它時(shí)一樣獲得樂趣。 現(xiàn)在,輪到你進(jìn)行實(shí)驗(yàn)了。 我很想看看你是否嘗試過這個(gè)以及你創(chuàng)造了什么。 與我分享您的代碼鏈接,我很樂意查看。
點(diǎn)贊 + 關(guān)注 + 收藏=學(xué)會(huì)了
在前端領(lǐng)域,如果只是懂 Vue 或者 React ,未來在職場(chǎng)的競(jìng)爭(zhēng)力可能會(huì)比較弱。
根據(jù)我多年在家待業(yè)經(jīng)驗(yàn)來看,前端未來在 數(shù)據(jù)可視化 和 AI 這兩個(gè)領(lǐng)域會(huì)比較香,而 Canvas 是數(shù)據(jù)可視化在前端方面的基礎(chǔ)技術(shù)。
本文就用光的速度將 canvas 給入門了。
01.gif
要入門一個(gè)技術(shù),前期最重要是快!所以本文只講入門內(nèi)容,能應(yīng)付簡(jiǎn)單項(xiàng)目。深入的知識(shí)點(diǎn)會(huì)在其他文章講解。
文章同時(shí)收錄于小程序-互聯(lián)網(wǎng)小兵,不止于前端,收各平臺(tái)優(yōu)質(zhì)熱門的技術(shù)文章(后端、移動(dòng)端、算法、人工智能...),大家支持支持,點(diǎn)擊前往體驗(yàn)!
CanvasSVG用JS動(dòng)態(tài)生成元素(一個(gè)HTML元素)用XML描述元素(類似HTML元素那樣,可用多個(gè)元素來描述一個(gè)圖形)位圖(受屏幕分辨率影響)矢量圖(不受屏幕分辨率影響)不支持事件支持事件數(shù)據(jù)發(fā)生變化需要重繪不需要重繪
就上面的描述而言可能有點(diǎn)難懂,你可以打開 AntV 旗下的圖形編輯引擎做對(duì)比。G6[1] 是使用 canvas 開發(fā)的,X6[2] 是使用 svg 開發(fā)的。
我的建議是:如果要展示的數(shù)據(jù)量比較大,比如一條數(shù)據(jù)就是一個(gè)元素節(jié)點(diǎn),那使用 canvas 會(huì)比較合適;如果用戶操作的交互比較多,而且對(duì)清晰度有要求(矢量圖),那么使用 svg 會(huì)比較合適。
學(xué)習(xí)前端一定要?jiǎng)邮智么a,然后看效果展示。
起步階段會(huì)用幾句代碼說明 canvas 如何使用,本例會(huì)畫一條直線。
02.png
<!-- 1、創(chuàng)建 canvas 元素 -->
<canvas
id="c"
width="300"
height="200"
style="border: 1px solid #ccc;"
></canvas>
<script>
// 2、獲取 canvas 對(duì)象
const cnv=document.getElementById('c')
// 3、獲取 canvas 上下文環(huán)境對(duì)象
const cxt=cnv.getContext('2d')
// 4、繪制圖形
cxt.moveTo(100, 100) // 起點(diǎn)坐標(biāo) (x, y)
cxt.lineTo(200, 100) // 終點(diǎn)坐標(biāo) (x, y)
cxt.stroke() // 將起點(diǎn)和終點(diǎn)連接起來
</script>
復(fù)制代碼
moveTo 、 lineTo 和 stroke 方法暫時(shí)可以不用管,它們的作用是繪制圖形,這些方法在后面會(huì)講到~
canvas 有 默認(rèn)的 寬度(300px) 和 高度(150px)
如果不在 canvas 上設(shè)置寬高,那 canvas 元素的默認(rèn)寬度是300px,默認(rèn)高度是150px。
canvas 元素提供了 width 和 height 兩個(gè)屬性,可設(shè)置它的寬高。
需要注意的是,這兩個(gè)屬性只需傳入數(shù)值,不需要傳入單位(比如 px 等)。
<canvas width="600" height="400"></canvas>
復(fù)制代碼
使用 css 設(shè)置 canvas 的寬高,會(huì)出現(xiàn) 內(nèi)容被拉伸 的后果?。。?/span>
03.png
<style>
#c {
width: 400px;
height: 400px;
border: 1px solid #ccc;
}
</style>
<canvas id="c"></canvas>
<script>
// 1、獲取canvas對(duì)象
const cnv=document.getElementById('c')
// 2、獲取canvas上下文環(huán)境對(duì)象
const cxt=cnv.getContext('2d')
// 3、繪制圖形
cxt.moveTo(100, 100) // 起點(diǎn)
cxt.lineTo(200, 100) // 終點(diǎn)
cxt.stroke() // 將起點(diǎn)和終點(diǎn)連接起來
console.log(cnv.width) // 獲取 canvas 的寬度,輸出:300
console.log(cnv.height) // 獲取 canvas 的高度,輸出:150
</script>
復(fù)制代碼
canvas 的默認(rèn)寬度是300px,默認(rèn)高度是150px。
最后出現(xiàn)的效果如上圖所示。
線條的默認(rèn)寬度是 1px ,默認(rèn)顏色是黑色。
但由于默認(rèn)情況下 canvas 會(huì)將線條的中心點(diǎn)和像素的底部對(duì)齊,所以會(huì)導(dǎo)致顯示效果是 2px 和非純黑色問題。
暫時(shí)只有 IE 9 以上才支持 canvas 。但好消息是 IE 已經(jīng)有自己的墓碑了。
如需兼容 IE 7 和 8 ,可以使用 ExplorerCanvas [3]。但即使是使用了 ExplorerCanvas 仍然會(huì)有所限制,比如無法使用 fillText() 方法等。
在繪制基礎(chǔ)圖形之前,需要先搞清除 Canvas 使用的坐標(biāo)系。
Canvas 使用的是 W3C 坐標(biāo)系 ,也就是遵循我們屏幕、報(bào)紙的閱讀習(xí)慣,從上往下,從左往右。
04.jpg
W3C 坐標(biāo)系 和 數(shù)學(xué)直角坐標(biāo)系 的 X軸 是一樣的,只是 Y軸 的反向相反。
W3C 坐標(biāo)系 的 Y軸 正方向向下。
最簡(jiǎn)單的起步方式是畫一條直線。這里所說的 “直線” 是幾何學(xué)里的 “線段” 的意思。
需要用到這3個(gè)方法:
起步階段可以先這樣理解。
05.png
<canvas id="c" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 繪制直線
cxt.moveTo(50, 100) // 起點(diǎn)坐標(biāo)
cxt.lineTo(200, 50) // 下一個(gè)點(diǎn)的坐標(biāo)
cxt.stroke() // 將上面的坐標(biāo)用一條線連接起來
</script>
復(fù)制代碼
上面的代碼所呈現(xiàn)的效果,可以看下圖解釋(手不太聰明,畫得不是很標(biāo)準(zhǔn),希望能看懂)
06.jpg
如需畫多條直線,可以用會(huì)上面那幾個(gè)方法。
07.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.stroke()
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.stroke()
</script>
復(fù)制代碼
仔細(xì)觀察一下,為什么兩條線的粗細(xì)不一樣的?
明明使用的方法都是一樣的,只是第二條直線的 Y軸 的值是有小數(shù)點(diǎn)。
答:默認(rèn)情況下 canvas 會(huì)將線條的中心點(diǎn)和像素的底部對(duì)齊,所以會(huì)導(dǎo)致顯示效果是 2px 和非純黑色問題。
08.jpg
上圖每個(gè)格子代表 1px。
線的中心點(diǎn)會(huì)和畫布像素點(diǎn)的底部對(duì)齊,所以會(huì)線中間是黑色的,但由于一個(gè)像素就不能再切割了,所以會(huì)有半個(gè)像素被染色,就變成了淺灰色。
所以如果你設(shè)置的 Y軸 值是一個(gè)整數(shù),就會(huì)出現(xiàn)上面那種情況。
09.png
<canvas id="c" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 繪制直線
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
// 修改直線的寬度
cxt.lineWidth=20
// 修改直線的顏色
cxt.strokeStyle='pink'
// 修改直線兩端樣式
cxt.lineCap='round' // 默認(rèn): butt; 圓形: round; 方形: square
cxt.stroke()
</script>
復(fù)制代碼
開辟新路徑的方法:
在繪制多條線段的同時(shí),還要設(shè)置線段樣式,通常需要開辟新路徑。
要不然樣式之間會(huì)相互污染。
比如這樣
10.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth=10
cxt.strokeStyle='pink'
cxt.stroke()
// 第二條線
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.stroke()
</script>
復(fù)制代碼
如果不想相互污染,需要做2件事:
如果上面2步卻了其中1步都會(huì)有影響。
11.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth=10
cxt.strokeStyle='pink'
cxt.stroke()
// 第二條線
cxt.beginPath() // 重新開啟一個(gè)路徑
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.stroke()
</script>
復(fù)制代碼
第一條線的樣式會(huì)影響之后的線。
但如果使用了 beginPath() ,后面的線段不會(huì)影響前面的線段。
12.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.stroke()
// 第二條線
cxt.beginPath() // 重新開啟一個(gè)路徑
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.lineWidth=4
cxt.strokeStyle='red'
cxt.stroke()
</script>
復(fù)制代碼
這個(gè)情況會(huì)反過來,后面的線能影響前面的線。
13.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 第一條線
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth=10
cxt.strokeStyle='pink'
cxt.stroke()
// 第二條線
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.lineWidth=4
cxt.strokeStyle='red'
cxt.stroke()
</script>
復(fù)制代碼
在設(shè)置 beginPath() 的同時(shí),也各自設(shè)置樣式。這樣就能做到相互不影響了。
14.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(20, 100)
cxt.lineTo(200, 100)
cxt.lineWidth=10
cxt.strokeStyle='pink'
cxt.stroke()
cxt.beginPath() // 重新開啟一個(gè)路徑
cxt.moveTo(20, 120.5)
cxt.lineTo(200, 120.5)
cxt.lineWidth=4
cxt.strokeStyle='red'
cxt.stroke()
</script>
復(fù)制代碼
和 直線 差不多,都是使用 moveTo() 、lineTo() 和 stroke() 方法可以繪制折線。
15.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(50, 200)
cxt.lineTo(100, 50)
cxt.lineTo(200, 200)
cxt.lineTo(250, 50)
cxt.stroke()
</script>
復(fù)制代碼
畫這種折線,最好在草稿紙上畫一個(gè)坐標(biāo)系,自己計(jì)算并描繪一下每個(gè)點(diǎn)大概在什么什么位置,最后在 canvas 中看看效果。
根據(jù)前面的基礎(chǔ),我們可以 使用線段來描繪矩形,但 canvas 也提供了 rect() 等方法可以直接生成矩形。
可以使用前面畫線段的方法來繪制矩形
16.png
canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 繪制矩形
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
cxt.lineTo(200, 120)
cxt.lineTo(50, 120)
cxt.lineTo(50, 50) // 需要閉合,又或者使用 closePath() 方法進(jìn)行閉合,推薦使用 closePath()
cxt.stroke()
</script>
復(fù)制代碼
上面的代碼幾個(gè)點(diǎn)分別對(duì)應(yīng)下圖。
17.jpg
18.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// strokeStyle 屬性
// strokeRect(x, y, width, height) 方法
cxt.strokeStyle='pink'
cxt.strokeRect(50, 50, 200, 100)
</script>
復(fù)制代碼
上面的代碼可以這樣理解
19.jpg
fillRect() 和 strokeRect() 方法差不多,但 fillRect() 的作用是填充。
需要注意的是,fillStyle 必須寫在 fillRect() 之前,不然樣式不生效。
20.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// fillStyle 屬性
// fillRect(x, y, width, height) 方法
cxt.fillStyle='pink'
cxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)
</script>
復(fù)制代碼
同時(shí)使用 strokeRect() 和 fillRect() 會(huì)產(chǎn)生描邊和填充的效果
21.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.strokeStyle='red'
cxt.strokeRect(50, 50, 200, 100) // strokeRect(x, y, width, height)
cxt.fillStyle='yellow'
cxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)
</script>
復(fù)制代碼
rect() 和 fillRect() 、strokeRect() 的用法差不多,唯一的區(qū)別是:
strokeRect() 和 fillRect() 這兩個(gè)方法調(diào)用后會(huì)立即繪制;rect() 方法被調(diào)用后,不會(huì)立刻繪制矩形,而是需要調(diào)用 stroke() 或 fill() 輔助渲染。
22.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.strokeStyle='red'
cxt.fillStyle='pink'
cxt.rect(50, 50, 200, 100) // rect(x, y, width, height)
cxt.stroke()
cxt.fill()
</script>
復(fù)制代碼
等價(jià)公式:
cxt.strokeStyle='red',
cxt.rect(50, 50, 200, 100)
cxt.stroke()
// 等價(jià)于
cxt.strokeStyle='red'
cxt.strokerect(50, 50, 200, 100)
// -----------------------------
cxt.fillStyle='hotpink'
cxt.rect(50, 50, 200, 100)
cxt.fill()
// 等價(jià)于
cxt.fillStyle='yellowgreen'
cxt.fillRect(50, 50, 200, 100)
復(fù)制代碼
使用 clearRect() 方法可以清空指定區(qū)域。
clearRect(x, y, width, height)
復(fù)制代碼
其語法和創(chuàng)建 cxt.rect() 差不多。
23.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.fillStyle='pink' // 設(shè)置填充顏色
cxt.fillRect(50, 50, 200, 200) // 填充矩形
cxt.clearRect(60, 60, 180, 90) // 清空矩形
</script>
復(fù)制代碼
canvas 畫布元素是矩形,所以可以通過下面的代碼把整個(gè)畫布清空掉。
// 省略部分代碼
cxt.clearRect(0, 0, cnv.width, cnv.height)
復(fù)制代碼
要清空的區(qū)域:從畫布左上角開始,直到畫布的寬和畫布的高為止。
\
Canvas 要畫多邊形,需要使用 moveTo() 、 lineTo() 和 closePath() 。
雖然三角形是常見圖形,但 canvas 并沒有提供類似 rect() 的方法來繪制三角形。
需要確定三角形3個(gè)點(diǎn)的坐標(biāo)位置,然后使用 stroke() 或者 fill() 方法生成三角形。
24.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
cxt.lineTo(200, 200)
// 注意點(diǎn):如果使用 lineTo 閉合圖形,是不能很好閉合拐角位的。
cxt.lineTo(50, 50) // 閉合
cxt.stroke()
</script>
復(fù)制代碼
注意,默認(rèn)情況下不會(huì)自動(dòng)從最后一個(gè)點(diǎn)連接到起點(diǎn)。最后一步需要設(shè)置一下 cxt.lineTo(50, 50) ,讓它與 cxt.moveTo(50, 50) 一樣。這樣可以讓路徑回到起點(diǎn),形成一個(gè)閉合效果。
但這樣做其實(shí)是有點(diǎn)問題的,而且也比較麻煩,要記住起始點(diǎn)坐標(biāo)。
上面的閉合操作,如果遇到設(shè)置了 lineWidth 或者 lineJoin 就會(huì)有問題,比如:
25.png
// 省略部分代碼
cxt.lineWidth=20
復(fù)制代碼
當(dāng)線段變粗后,起始點(diǎn)和結(jié)束點(diǎn)的鏈接處,拐角就出現(xiàn)“不正?!爆F(xiàn)象。
如果需要真正閉合,可以使用 closePath() 方法。
26.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
cxt.lineTo(200, 200)
// 手動(dòng)閉合
cxt.closePath()
cxt.lineJoin='miter' // 線條連接的樣式。miter: 默認(rèn); bevel: 斜面; round: 圓角
cxt.lineWidth=20
cxt.stroke()
</script>
復(fù)制代碼
使用 cxt.closePath() 可以自動(dòng)將終點(diǎn)和起始點(diǎn)連接起來,此時(shí)看上去就正常多了。
有一組鄰邊相等的平行四邊形是菱形
27.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(150, 50)
cxt.lineTo(250, 100)
cxt.lineTo(150, 150)
cxt.lineTo(50, 100)
cxt.closePath()
cxt.stroke()
</script>
復(fù)制代碼
要繪制直線類型的圖形,在草稿紙上標(biāo)記出起始點(diǎn)和每個(gè)拐角的點(diǎn),然后再連線即可。相對(duì)曲線圖形來說,直線圖形是比較容易的。
繪制圓形的方法是 arc()。
語法:
arc(x, y, r, sAngle, eAngle,counterclockwise)
復(fù)制代碼
28.jpg
開始角度和結(jié)束角度,都是以弧度為單位。例如 180°就寫成 Math.PI ,360°寫成 Math.PI * 2 ,以此類推。
在實(shí)際開發(fā)中,為了讓自己或者別的開發(fā)者更容易看懂弧度的數(shù)值,1°應(yīng)該寫成 Math.PI / 180。
注意:繪制圓形之前,必須先調(diào)用 beginPath() 方法!!!在繪制完成之后,還需要調(diào)用 closePath() 方法?。。?/strong>
29.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 80, 0, 360 * Math.PI / 180)
cxt.closePath()
cxt.stroke()
</script>
復(fù)制代碼
如果使用 arc() 方法畫圓時(shí),沒做到剛好繞完一周(360°)就直接閉合路徑,就會(huì)出現(xiàn)半圓的狀態(tài)。
30.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180) // 順時(shí)針
cxt.closePath()
cxt.stroke()
</script>
復(fù)制代碼
上面的代碼中,cxt.arc 最后一個(gè)參數(shù)沒傳,默認(rèn)是 false ,所以是順時(shí)針繪制。
31.jpg
如果希望半圓的弧面在上方,可以將 cxt.arc 最后一個(gè)參數(shù)設(shè)置成 true
32.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180, true)
cxt.closePath()
cxt.stroke()
</script>
復(fù)制代碼
使用 arc() 方法畫半圓時(shí),如果最后不調(diào)用 closePath() 方法,就不會(huì)出現(xiàn)閉合路徑。也就是說,那是一條弧線。
在 canvas 中,畫弧線有2中方法:arc() 和 arcTo() 。
如果想畫一條 0° ~ 30° 的弧線,可以這樣寫
33.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.beginPath()
cxt.arc(150, 150, 100, 0, 30 * Math.PI / 180)
cxt.stroke()
</script>
復(fù)制代碼
原理如下圖所示,紅線代表畫出來的那條弧線。
34.jpg
arcTo() 的使用方法會(huì)更加復(fù)雜,如果初學(xué)看不太懂的話可以先跳過,看完后面的再回來補(bǔ)補(bǔ)。
語法:
arcTo(cx, cy, x2, y2, radius)
復(fù)制代碼
其中,(cx, cy) 也叫控制點(diǎn),(x2, y2) 也叫結(jié)束點(diǎn)。
是不是有點(diǎn)奇怪,為什么沒有 x1 和 y1 ?
(x1, y1) 是開始點(diǎn),通常是由 moveTo() 或者 lineTo() 提供。
arcTo() 方法利用 開始點(diǎn)、控制點(diǎn)和結(jié)束點(diǎn)形成的家教,繪制一段與家教的兩邊相切并且半徑為 radius 的圓弧。
35.jpg
舉個(gè)例子
36.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(40, 40)
cxt.arcTo(120, 40, 120, 80, 80)
cxt.stroke()
</script>
復(fù)制代碼
前面學(xué)完基礎(chǔ)圖形,接下來可以開始了解一下如何設(shè)置元素的基礎(chǔ)樣式。
前面的案例中,其實(shí)已經(jīng)知道使用 stroke() 方法進(jìn)行描邊了。這里就不再多講這個(gè)方法。
lineWidth 默認(rèn)值是 1 ,默認(rèn)單位是 px。
語法:
lineWidth=線寬
復(fù)制代碼
37.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 線寬 10
cxt.beginPath()
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineWidth=10 // 設(shè)置線寬
cxt.stroke()
// 線寬 20
cxt.beginPath()
cxt.moveTo(50, 150)
cxt.lineTo(250, 150)
cxt.lineWidth=20 // 設(shè)置線寬
cxt.stroke()
// 線寬 30
cxt.beginPath()
cxt.moveTo(50, 250)
cxt.lineTo(250, 250)
cxt.lineWidth=30 // 設(shè)置線寬
cxt.stroke()
</script>
復(fù)制代碼
使用 strokeStyle 可以設(shè)置線條顏色
語法:
strokeStyle=顏色值
復(fù)制代碼
38.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineWidth=20
cxt.strokeStyle='pink' // 設(shè)置顏色
cxt.stroke()
</script>
復(fù)制代碼
為了展示方便,我將 lineWidth 設(shè)為 20。
線帽指的是線段的開始和結(jié)尾處的樣式,使用 lineCap 可以設(shè)置
語法:
lineCap='屬性值'
復(fù)制代碼
屬性值包括:
39.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 設(shè)置線寬,方便演示
cxt.lineWidth=16
// 默認(rèn)線帽 butt
cxt.beginPath()
cxt.moveTo(50, 60)
cxt.lineTo(250, 60)
cxt.stroke()
// 方形線帽 square
cxt.beginPath()
cxt.lineCap='square'
cxt.moveTo(50, 150)
cxt.lineTo(250, 150)
cxt.stroke()
// 圓形線帽 round
cxt.beginPath()
cxt.lineCap='round'
cxt.moveTo(50, 250)
cxt.lineTo(250, 250)
cxt.stroke()
</script>
復(fù)制代碼
使用 square 和 round 的話,會(huì)使線條變得稍微長(zhǎng)一點(diǎn)點(diǎn),這是給線條增加線帽的部分,這個(gè)長(zhǎng)度在日常開發(fā)中需要注意。
線帽只對(duì)線條的開始和結(jié)尾處產(chǎn)生作用,對(duì)拐角不會(huì)產(chǎn)生任何作用。
如果需要設(shè)置拐角樣式,可以使用 lineJoin 。
語法:
lineJoin='屬性值'
復(fù)制代碼
屬性值包括:
40.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.lineWidth=20
// 默認(rèn),尖角
cxt.moveTo(50, 40)
cxt.lineTo(200, 40)
cxt.lineTo(200, 90)
cxt.stroke()
// 斜角 bevel
cxt.beginPath()
cxt.moveTo(50, 140)
cxt.lineTo(200, 140)
cxt.lineTo(200, 190)
cxt.lineJoin='bevel'
cxt.stroke()
// 圓角 round
cxt.beginPath()
cxt.moveTo(50, 240)
cxt.lineTo(200, 240)
cxt.lineTo(200, 290)
cxt.lineJoin='round'
cxt.stroke()
</script>
復(fù)制代碼
使用 setLineDash() 方法可以將描邊設(shè)置成虛線。
語法:
setLineDash([])
復(fù)制代碼
需要傳入一個(gè)數(shù)組,且元素是數(shù)值型。
虛線分3種情況
41.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.lineWidth=20
cxt.strokeStyle='pink'
cxt.moveTo(50, 50)
cxt.lineTo(200, 50)
cxt.setLineDash([10]) // 只傳1個(gè)參數(shù),實(shí)線與空白都是 10px
cxt.stroke()
cxt.beginPath()
cxt.moveTo(50, 100)
cxt.lineTo(200, 100)
cxt.setLineDash([10, 20]) // 2個(gè)參數(shù),此時(shí),實(shí)線是 10px, 空白 20px
cxt.stroke()
cxt.beginPath()
cxt.moveTo(50, 150)
cxt.lineTo(200, 150)
cxt.setLineDash([10, 20, 5]) // 傳3個(gè)以上的參數(shù),此例:10px實(shí)線,20px空白,5px實(shí)線,10px空白,20px實(shí)線,5px空白 ……
cxt.stroke()
</script>
復(fù)制代碼
此外,還可以始終 cxt.getLineDash() 獲取虛線不重復(fù)的距離;
用 cxt.lineDashOffset 設(shè)置虛線的偏移位。
使用 fill() 可以填充圖形,根據(jù)前面的例子應(yīng)該掌握了如何使用 fill()
42.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.fillStyle='pink'
cxt.rect(50, 50, 200, 100)
cxt.fill()
</script>
復(fù)制代碼
可以使用 fillStyle 設(shè)置填充顏色,默認(rèn)是黑色。
在使用 fill() 方法填充時(shí),需要注意一個(gè)規(guī)則:非零環(huán)繞填充。
在使用 moveTo 和 lineTo 描述圖形時(shí),如果是按順時(shí)針繪制,計(jì)數(shù)器會(huì)加1;如果是逆時(shí)針,計(jì)數(shù)器會(huì)減1。
當(dāng)圖形所處的位置,計(jì)數(shù)器的結(jié)果為0時(shí),它就不會(huì)被填充。
這樣說有點(diǎn)復(fù)雜,先看看例子
43.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 外層矩形
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineTo(250, 250)
cxt.lineTo(50, 250)
cxt.closePath()
// 內(nèi)層矩形
cxt.moveTo(200, 100)
cxt.lineTo(100, 100)
cxt.lineTo(100, 200)
cxt.lineTo(200, 200)
cxt.closePath()
cxt.fill()
</script>
復(fù)制代碼
請(qǐng)看看上面的代碼,我畫了2個(gè)矩形,它們都沒有用 beginPath() 方法開辟新路徑。
44.png
內(nèi)層矩形是逆時(shí)針繪制的,所以內(nèi)層的值是 -1 ,它又經(jīng)過外層矩形,而外層矩形是順時(shí)針繪制,所以經(jīng)過外層時(shí)值 +1,最終內(nèi)層的值為 0 ,所以不會(huì)被填充。
Canvas 提供了一些操作文本的方法。
為了方便演示,我們先了解一下在 Canvas 中如何給本文設(shè)置樣式。
和 CSS 設(shè)置 font 差不多,Canvas 也可以通過 font 設(shè)置樣式。
語法:
cxt.font='font-style font-variant font-weight font-size/line-height font-family'
復(fù)制代碼
如果需要設(shè)置字號(hào) font-size,需要同事設(shè)置 font-family。
cxt.font='30px 宋體'
復(fù)制代碼
使用 strokeText() 方法進(jìn)行文本描邊
語法:
strokeText(text, x, y, maxWidth)
復(fù)制代碼
45.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.font='60px Arial' // 將字號(hào)設(shè)置成 60px,方便觀察
cxt.strokeText('雷猴', 30, 90)
</script>
復(fù)制代碼
使用 strokeStyle 設(shè)置描邊顏色。
46.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.font='60px Arial' // 將字號(hào)設(shè)置成 60px,方便觀察
cxt.strokeStyle='pink' // 設(shè)置文本描邊顏色
cxt.strokeText('雷猴', 30, 90)
</script>
復(fù)制代碼
使用 fillText() 可填充文本。
語法和 strokeText() 一樣。
fillText(text, x, y, maxWidth)
復(fù)制代碼
47.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.font='60px Arial'
cxt.fillText('雷猴', 30, 90)
</script>
復(fù)制代碼
使用 fillStyle 可以設(shè)置文本填充顏色。
48.png
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
cxt.font='60px Arial'
cxt.fillStyle='pink'
cxt.fillText('雷猴', 30, 90)
</script>
復(fù)制代碼
measureText().width 方法可以獲取文本的長(zhǎng)度,單位是 px 。
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
let text='雷猴'
cxt.font='bold 40px Arial'
cxt.fillText(text, 40, 80)
console.log(cxt.measureText(text).width) // 80
</script>
復(fù)制代碼
使用 textAlign 屬性可以設(shè)置文字的水平對(duì)齊方式,一共有5個(gè)值可選
49.png
紅線是輔助參考線。
<canvas id="c" width="400" height="400" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 豎向的輔助線(參考線,在畫布中間)
cxt.moveTo(200, 0)
cxt.lineTo(200, 400)
cxt.strokeStyle='red'
cxt.stroke()
cxt.font='30px Arial'
// 橫坐標(biāo)開始位對(duì)齊
cxt.textAlign='start' // 默認(rèn)值,
cxt.fillText('雷猴 start', 200, 40)
// 橫坐標(biāo)結(jié)束位對(duì)齊
cxt.textAlign='end' // 結(jié)束對(duì)齊
cxt.fillText('雷猴 end', 200, 100)
// 左對(duì)齊
cxt.textAlign='left' // 左對(duì)齊
cxt.fillText('雷猴 left', 200, 160)
// 右對(duì)齊
cxt.textAlign='right' // 右對(duì)齊
cxt.fillText('雷猴 right', 200, 220)
// 居中對(duì)齊
cxt.textAlign='center' // 右對(duì)齊
cxt.fillText('雷猴 center', 200, 280)
</script>
復(fù)制代碼
從上面的例子看,start 和 left 的效果好像是一樣的,end 和 right 也好像是一樣的。
在大多數(shù)情況下,它們的確一樣。但在某些國(guó)家或者某些場(chǎng)合,閱讀文字的習(xí)慣是 從右往左 時(shí),start 就和 right 一樣了,end 和 left 也一樣。這是需要注意的地方。
使用 textBaseline 屬性可以設(shè)置文字的垂直對(duì)齊方式。
在使用 textBaseline 前,需要自行了解 css 的文本基線。
50.png
用一張網(wǎng)圖解釋一下基線
textBaseline 可選屬性:
51.png
紅線是輔助參考線。
<canvas id="c" width="800" height="300" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 橫向的輔助線(參考線,在畫布中間)
cxt.moveTo(0, 150)
cxt.lineTo(800, 150)
cxt.strokeStyle='red'
cxt.stroke()
cxt.font='20px Arial'
// 默認(rèn) alphabetic
cxt.textBaseline='alphabetic'
cxt.fillText('雷猴 alphabetic', 10, 150)
// 默認(rèn) top
cxt.textBaseline='top'
cxt.fillText('雷猴 top', 200, 150)
// 默認(rèn) bottom
cxt.textBaseline='bottom'
cxt.fillText('雷猴 bottom', 320, 150)
// 默認(rèn) middle
cxt.textBaseline='middle'
cxt.fillText('雷猴 middle', 480, 150)
// 默認(rèn) hanging
cxt.textBaseline='hanging'
cxt.fillText('雷猴 hanging', 640, 150)
</script>
復(fù)制代碼
注意:在繪制文字的時(shí)候,默認(rèn)是以文字的左下角作為參考點(diǎn)進(jìn)行繪制
在 Canvas 中可以使用 drawImage() 方法繪制圖片。
渲染圖片的方式有2中,一種是在JS里加載圖片再渲染,另一種是把DOM里的圖片拿到 canvas 里渲染。
渲染的語法:
drawImage(image, dx, dy)
復(fù)制代碼
在 JS 里加載圖片并渲染,有以下幾個(gè)步驟:
52.png
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
// 1 創(chuàng)建 Image 對(duì)象
const image=new Image()
// 2 引入圖片
image.src='./images/dog.jpg'
// 3 等待圖片加載完成
image.onload=()=> {
// 4 使用 drawImage() 方法渲染圖片
cxt.drawImage(image, 30, 30)
}
</script>
復(fù)制代碼
53.png
<style>
#dogImg {
display: none;
}
</style>
<img src="./images/dog.jpg" id="dogImg"/>
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
const image=document.getElementById('dogImg')
cxt.drawImage(image, 70, 70)
</script>
復(fù)制代碼
因?yàn)閳D片是從 DOM 里獲取到的,所以一般來說,只要在 window.onload 這個(gè)生命周期內(nèi)使用 drawImage 都可以正常渲染圖片。
本例使用了 css 的方式,把圖片的 display 設(shè)置成 none 。因?yàn)槲也幌氡?<img> 影響到本例講解。
實(shí)際開發(fā)過程中按照實(shí)際情況設(shè)置即可。
前面的例子都是直接加載圖片,圖片默認(rèn)的寬高是多少就加載多少。
如果需要指定圖片寬高,可以在前面的基礎(chǔ)上再添加兩個(gè)參數(shù):
drawImage(image, dx, dy, dw, dh)
復(fù)制代碼
image、 dx、 dy 的用法和前面一樣。
dw 用來定義圖片的寬度,dy 定義圖片的高度。
54.png
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
const image=new Image()
image.src='./images/dog.jpg'
image.onload=()=> {
cxt.drawImage(image, 30, 30, 100, 100)
}
</script>
復(fù)制代碼
我把圖片的尺寸設(shè)為 100px * 100px,圖片看上去比之前就小了很多。
截圖圖片同樣使用drawImage() 方法,只不過傳入的參數(shù)數(shù)量比之前都多,而且順序也有點(diǎn)不一樣了。
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
復(fù)制代碼
以上參數(shù)缺一不可
55.png
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas>
<script>
const cnv=document.getElementById('c')
const cxt=cnv.getContext('2d')
const image=new Image()
image.src='./images/dog.jpg'
image.onload=()=> {
cxt.drawImage(image, 0, 0, 100, 100, 30, 30, 200, 200)
}
</script>
復(fù)制代碼
本文主要講解了在 Canvas 中繪制一些基礎(chǔ)圖形,還有一些基礎(chǔ)樣式設(shè)置。
還有更多高級(jí)的玩法會(huì)在之后的文章中講到,比如漸變、投影、濾鏡等等。
德育處主任,https://juejin.cn/post/7116784455561248775
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。