canvas系列教程04 —— 渐变、阴影、路径、状态、Canvas对象、图形重叠模式
漸變
線性漸變
在一條直線上進行漸變
- x1、y1表示漸變色開始點的坐標
- x2、y2表示漸變色結束點的坐標
- value1 漸變開始的位置,取值 0~1
- color1 漸變開始的顏色
- value2 漸變結束的位置,取值 0~1
- color2 漸變結束的顏色
第2和第3步可以反復多次添加多個漸變,每次漸變都以上一個漸變的結束位置為開始位置
fill()可以改為fillRect()或fillText()。其中fillRect()表示矩形漸變,fillText()表示文字漸變。
var gnt = cxt.createLinearGradient(0,150, 200, 150);gnt.addColorStop(0, "HotPink");gnt.addColorStop(1, "white");cxt.fillStyle = gnt;cxt.fillRect(0, 0, 200, 150); var text = "綠葉學習網";cxt.font = "bold 50px 微軟雅黑";var gnt = cxt.createLinearGradient(0, 75, 200, 75);gnt.addColorStop(0, "HotPink");gnt.addColorStop(1, "LightSkyBlue");cxt.fillStyle = gnt;cxt.fillText(text, 10, 90);徑向漸變(用得少)
徑向漸變,是一種從起點到終點、顏色從內到外進行的圓形漸變(從中間向外拉,像圓一樣)。徑向漸變是圓形漸變或橢圓形漸變,顏色不再沿著一條直線漸變,而是從一個起點向所有方向漸變。
- (x1,y1)表示漸變開始圓心的坐標,r1表示漸變開始圓心的半徑。
- (x2,y2)表示漸變結束圓心的坐標,r2表示漸變結束圓的半徑。
當起點圓與終點圓的圓心坐標相同時,會有一種圓形漸變的效果
- value1 漸變開始的位置,取值 0~1
- color1 漸變開始的顏色
- value2 漸變結束的位置,取值 0~1
- color2 漸變結束的顏色
第2和第3步可以反復多次添加多個漸變,每次漸變都以上一個漸變的結束位置為開始位置
fill()可以改為fillRect()或fillText()。其中fillRect()表示矩形漸變,fillText()表示文字漸變。
//畫圓cxt.beginPath();cxt.arc(80, 80, 50, 0, Math.PI * 2, true);cxt.closePath();//漸變var gnt = cxt.createRadialGradient(100, 60, 10, 80, 80, 50);gnt.addColorStop(0, "white");gnt.addColorStop(0.9, "orange");gnt.addColorStop(1, "rgba(0,0,0,0)");//填充cxt.fillStyle = gnt;cxt.fill(); gradient = cxt.createRadialGradient(60, 60, 0, 60, 60, 60);gradient.addColorStop("0", "magenta");gradient.addColorStop("0.25", "blue");gradient.addColorStop("0.50", "green");gradient.addColorStop("0.75", "yellow");gradient.addColorStop("1.0", "HotPink");cxt.fillStyle = gradient;cxt.fillRect(0, 0, 120, 120); var i = 0;setInterval(function () {gradient = cxt.createRadialGradient(60, 60, 0, 60, 60, 60);gradient.addColorStop(i * 0, "magenta");gradient.addColorStop(i * 0.25, "blue");gradient.addColorStop(i * 0.50, "green");gradient.addColorStop(i * 0.75, "yellow");gradient.addColorStop(i * 1.0, "HotPink");cxt.fillStyle = gradient;i = i + 0.1;if (i >= 1) { //超過顏色點值后,自動歸0i = 0;}cxt.fillRect(0, 0, 120, 120);}, 50);陰影
| shadowOffsetX | 陰影與圖形的水平距離,默認值為0。大于0時向右偏移,小于0時向左偏移 |
| shadowOffsetY | 陰影與圖形的垂直距離,默認值為0。大于0時向下偏移,小于0時向上偏移 |
| shadowColor | 陰影的顏色,默認值為黑色 |
| shadowBlur | 陰影的模糊值,默認值為0。該值越大,模糊度越強;該值越小,模糊度越弱 |
Canvas陰影屬性使用的也是W3C坐標系,y 軸向下
四個方向的陰影效果,只需要shadowOffsetX和shadowOffsetY這兩個屬性的值都定義為0就可以了。
路徑
| beginPath() | 開始一條新的路徑 |
| closePath() | 關閉當前路徑 |
| isPointInPath() | 判斷某一個點是否存在于當前路徑內 |
beginPath()
cxt.beginPath();用于開始一個新路徑(同時也是結束上一個路徑),剛開始繪圖時,其實都默認執行了一次 beginPath() 開始了一個新路徑,直到再次遇到 beginPath() 才會結束默認創建的路徑,而開始一段新路徑。
Canvas基于“狀態”繪制圖形。每一次繪制(stroke()或fill()),Canvas會檢測整個程序定義的所有狀態,這些狀態包括strokeStyle、fillStyle、lineWidth等。當一個狀態值沒有被改變時,Canvas就一直使用最初的值,當一個狀態值被改變時,需要分兩種情況考慮。
(1)如果使用beginPath()開始一個新的路徑,則不同路徑使用不同的值。
(2)如果沒有使用beginPath()開始一個新的路徑,則后面的值會覆蓋前面的值(后來者居上原則)。
cxt.lineWidth = 5;//第1條直線cxt.moveTo(50, 40);cxt.lineTo(150, 40);cxt.strokeStyle = "red";cxt.stroke();//第2條直線cxt.moveTo(50, 80);cxt.lineTo(150, 80);cxt.strokeStyle = "green";cxt.stroke();//第3條直線cxt.moveTo(50, 120);cxt.lineTo(150, 120);cxt.strokeStyle = "blue";cxt.stroke();
三條直線都屬于同一個路徑,所以cxt.strokeStyle=‘green’;會覆蓋cxt.strokeStyle=‘red’;,然后cxt.strokeStyle=‘blue’;會覆蓋cxt.strokeStyle=‘green’;。因此strokeStyle屬性最終取值為blue,即三條直線都是blue。
使用beginPath()后,三條直線將位于不同的路徑中。因此,不同路徑中定義的狀態不會像上一個例子那樣發生覆蓋。
判斷是否屬于同一路徑的標準是:是否使用了beginPath(),而不是視覺上是否有首尾連線。
Canvas中的繪制方法如stroke()、fill()等,都是以“之前最近的beginPath()”后面所有定義的狀態為基礎進行繪制的。
closePath()
cxt.closePath();用于關閉路徑,即將同一個路徑的起點與終點連接起來,使其成為一個封閉的圖形。
常用于繪制多邊形的最后一步。
如果Canvas只有一條線段的話,那么closePath()方法就什么都不做。
cxt.arc(70, 70, 50, 0, -90 * Math.PI / 180, true);cxt.stroke();cxt.closePath(); cxt.lineWidth = 10;cxt.strokeStyle = "HotPink";cxt.moveTo(40, 60);cxt.lineTo(100, 60);cxt.lineTo(100, 30);cxt.lineTo(150, 75);cxt.lineTo(100, 120);cxt.lineTo(100, 90);cxt.lineTo(40, 90);cxt.lineTo(40, 60);cxt.stroke();
將lineWidth定義得足夠大(10px)時,如果使用lineTo()方法來關閉圖形,會有一個如圖所示的“缺口”小問題。有兩種方法可以解決:
isPointInPath()
cxt.isPointInPath(x , y);用于判斷點 x,y 是否在當前路徑中,若在返回true,否則返回false,僅對 rect()方法有效,對 strokeRect() 和 fillRect() 無效。
cxt.strokeStyle = "HotPink";cxt.rect(50, 50, 80, 80);cxt.stroke();if (cxt.isPointInPath(100, 50)) {alert("點(100,100)存在于當前路徑中");} cxt.moveTo(50, 50);cxt.lineTo(150, 50);cxt.stroke();if (cxt.isPointInPath(100, 50)) {alert("點(50,100)存在于當前路徑中");}上面代碼只在IE瀏覽器中運行中有效果,但是在Google瀏覽器和Firefox中不會彈出對話框。實際上,當我們想要使用isPointInPath()方法判斷某個點是否位于一條直線上時,在Goole和Firefox瀏覽器中都是無法實現的。不過可以使用isPointInPath()方法判斷某個點是否位于一個圖形(如矩形、圓形等)上面。
狀態
Canvas基于“狀態”來繪制圖形。每一次繪制(stroke()或fill()),Canvas會檢測整個程序定義的所有狀態,這些狀態包括strokeStyle、fillStyle、lineWidth等。當一個狀態值沒有被改變時,Canvas就會一直使用最初的值。當一個狀態值被改變時,分兩種情況考慮:
(1)如果使用beginPath()開始一個新的路徑,則不同路徑使用不同的值。
(2)如果沒有使用beginPath()開始一個新的路徑,則后面的值會覆蓋前面的值(后來者居上原則)。
狀態的保存和恢復
Canvas提供了兩個操作狀態的方法:
- save() 保存“當前狀態”,如剪切狀態、變換狀態和繪圖狀態(各種繪圖樣式屬性),不能保存路徑狀態(開始新的路徑,只有beginPath()一個方法),也不能保存圖形(Canvas只有當前一個上下文環境,如果想要恢復圖形,就只能清空畫布再重繪。)。
- restore() 恢復“之前保存的狀態”。
save()和restore()一般情況下都是成對配合使用,使用場景如下:
(1)圖形或圖片裁切。
(2)圖形或圖片變換。
(3)以下屬性改變的時候:fillStyle、font、globalAlpha、globalCompositeOperation、lineCap、lineJoin、lineWidth、miterLimit、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、strokeStyle、textAlign、textBaseline。
應用一:清除剪切區域
創建剪切區域 clip()
創建剪切區域后,繪制的圖形都只限于這個剪切區域之內,超出剪切區域的部分不會顯示(被剪切掉了)。
把整個畫布(Canvas)看成一個房子,clip()方法的剪切區域則可以看成一扇窗戶。即使房子再大,最終透過窗戶所能看到的空間也就只有窗戶這么大。
cxt.clip();clip()方法也不支持Canvas自帶的兩個方法:strokeRect()、fillRect()。如果要使用strokeRect()和fillRect(),請使用rect()方法來代替。
//繪制一個"描邊圓",圓心為(50,50),半徑為40cxt.beginPath();cxt.arc(50, 50, 40, 0, 360 * Math.PI / 180, true);cxt.closePath();cxt.strokeStyle = "HotPink";cxt.stroke();//使用clip(),使得"描邊圓"成為一個剪切區域cxt.clip();//繪制一個"填充矩形"cxt.beginPath();cxt.fillStyle = "#66CCFF";cxt.fillRect(50, 50, 100, 80);
若注釋掉 cxt.clip();
清除剪切區域
在圖形或者圖片剪切(clip())之前使用save()方法來保持當前狀態,然后在剪切(clip())之后使用restore()方法恢復之前保存的狀態。
如果不使用save()和restore(),即便使用clearRect()方法清空畫布,后面繪制的所有圖形或圖片也都會限制在這個剪切區域內。
//save()保存狀態cxt.save();//使用clip()方法指定一個圓形的剪切區域cxt.beginPath();cxt.arc(70, 70, 50, 0, 360 * Math.PI / 180, true);cxt.closePath();cxt.stroke();cxt.clip();//繪制一張圖片var image = new Image();image.src = "images/princess.png";image.onload = function () {cxt.drawImage(image, 10, 20);}$$("btn").onclick = function () {//restore()恢復狀態cxt.restore();//清空畫布cxt.clearRect(0, 0, cnv.width, cnv.height);//繪制一張新圖片var image = new Image();image.src = "images/Judy.png";image.onload = function () {cxt.drawImage(image, 10, 20);}}
刪除 cxt.save()和cxt.restore() 后效果為:
應用二:圖片/圖形的變換
cxt.fillStyle = "HotPink";cxt.translate(30, 30);cxt.fillRect(0, 0, 100, 50);cxt.fillStyle = "LightSkyBlue ";cxt.translate(60, 60);cxt.fillRect(0, 0, 100, 50);
藍色矩形預期坐標為 (60,60),最終效果卻是 (90,90),因為之前 translate(30, 30),整個坐標系的原點已經發生了位移變化 !
解決方案是使用 save()和restore()來實現
cxt.save();cxt.fillStyle = "HotPink";cxt.translate(30, 30);cxt.fillRect(0, 0, 100, 50);cxt.restore();cxt.fillStyle = "LightSkyBlue ";cxt.translate(60, 60);cxt.fillRect(0, 0, 100, 50);
在變換操作(平移、縮放、旋轉)中,一般都是在操作之前使用save()方法保存當前狀態,其中當前狀態包括參考坐標、圖形大小等。然后再使用restore()方法來恢復之前保存的狀態。
Canvas對象
屬性
- globalAlpha 透明度
默認值為1.0(完全不透明),取值范圍為:0.0~1.0。其中0.0表示完全透明,1.0表示完全不透明。globalAlpha屬性必須在圖形繪制之前定義才有效。 - width 寬度
- height 高度
常用于文字水平居中對齊和清空畫布
cxt.clearRect(0, 0, cnv.width, cnv.height);方法
- getContext(“2d”) 獲取Canvas 2D上下文環境對象
- toDataURL() 獲取Canvas對象產生的位圖的字符串,即將Canvas畫布轉換為圖片的base64格式的字符串。
- type 可選,默認值為image/png類型
直接在Canvas畫布上點擊鼠標右鍵,在彈出的快捷菜單中也能“另存為”圖片為本地圖片,為什么還要那么麻煩地使用toDataURL()呢?
事實上,很多舊版本的瀏覽器并不具備這個功能。因此為了兼容性,建議使用toDataURL()方法進行處理。
- canvas保存圖片時,谷歌瀏覽器Chrome報錯【解決方案】Not allowed to navigate top frame to data URL
https://blog.csdn.net/weixin_41192489/article/details/124386704
圖形的重疊模式
默認情況下,后繪制的圖形會覆蓋之前繪制的圖形。
通過 globalCompositeOperation 可以修改重疊模式
cxt.globalCompositeOperation = 屬性值;不同屬性值,效果如下圖:
| source-over | 默認值,新圖形覆蓋舊圖形 |
| copy | 只顯示新圖形,舊圖形作透明處理 |
| darker | 兩種圖形都顯示,在重疊部分,顏色由兩個圖形的顏色值相減后形成 |
| destination-atop | 只顯示新圖形與舊圖形重疊部分以及新圖形的其余部分,其他部分作透明處理 |
| destination-in | 只顯示舊圖形中與新圖形重疊部分,其他部分作透明處理 |
| destination-out | 只顯示舊圖形中與新圖形不重疊部分,其他部分作透明處理 |
| destination-over | 與source-over屬性相反,舊圖形覆蓋新圖形 |
| lighter | 兩種圖形都顯示,在圖形重疊部分,顏色由兩個圖形的顏色值相加后形成 |
| source-atop | 只顯示舊與新圖形重疊部分及舊圖形的其余部分,其他部分作透明處理 |
| source-in | 只顯示新圖形中與舊圖形重疊部分,其他部分作透明處理 |
| source-out | 只顯示新圖形中與舊圖形不重疊部分,其余部分作透明處理 |
| xor | 兩種圖形都繪制,其中重疊部分透明處理 |
總結
以上是生活随笔為你收集整理的canvas系列教程04 —— 渐变、阴影、路径、状态、Canvas对象、图形重叠模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 仿人机器人的跑步研究学习笔记1之机器人的
- 下一篇: 声学机器学习:理论和应用 (Machin