全文說到了三角形,圓形等相關圖形的畫法,不熟悉的同學可以出門右轉,先看看前文,接下來繼續我們的圖形——曲線。
學過數學,或者是比較了解js 的同學都知道貝塞爾曲線,當然,在數學裡面,這是一門高深的學問,js裡面的貝塞爾曲線一般是用來做動畫的,其實別的地方也有體現,比如說Photoshop裡面的鋼筆工具,CorelDraw裡面的貝塞爾工具等等,canvas中,也是有體現的
當然,如果是單純的畫一條曲線,也可以用前面的方法:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.arc(100,100,100,0,90*Math.PI/180,false); ctx.stroke(); ctx.beginPath(); ctx.moveTo(103,103); ctx.arcTo(183,83,162,182,40); ctx.stroke();
如果要畫一個彎彎曲曲的線條就費勁了,這才有下面的主角登場:
quadraticCurveTo(cpx,cpy,x,y) 二次貝塞爾曲線
參數:cpx,cpy 表示第一個控制點,x,y 表示結束點
加上起點,就是3個點控制一條曲線,其實這個跟arcTo的用法差不多,不同的地方就是arcTo是需要指定圓弧的半徑的,因為它是2條線中畫一個圓與直線的切點形成的曲線,那這個二次貝塞爾曲線的畫圖原理是什麼呢?咱們一起來畫一畫:
二次貝塞爾曲線的大致規律:從起始點出發,曲線越靠近控制點,曲線越陡,然後慢慢遠離控制點,曲線隨即越來越平緩,直到結束點,並且此曲線會與起始點和結束點相切
這個控制點是不是有點像一個磁鐵一樣,吸引著這條曲線的運動,俗話說耳聞不如一見,咱們試一下:
ctx.moveTo(50,50); ctx.lineTo(70,120); ctx.lineTo(200,80); ctx.stroke(); ctx.beginPath(); ctx.moveTo(50,50); ctx.quadraticCurveTo(70,120,200,80); ctx.stroke();
看看,是不是這樣的,當然,曲線彎曲的程度是多少是有公式的,但是我們不需要關心,只需要記住一點就夠了:曲線靠近控制點,曲線越陡,遠離控制點,曲線越平,哦了!
再次提示一下,arcTo與quadraticCurveTo的區別,現在是否明白?
現在我們來介紹一下三次貝塞爾曲線:
bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x,y) 三次貝塞爾曲線
參數:cpx1,cpy1表示第一個控制點,cpx2,cpy2表示第二個控制點 x,y表示結束點
包括起始點一起4個點來決定一條曲線,這個跟二次貝塞爾曲線的原理是一樣一樣的,只是多一個控制點,其精髓還是那句話:曲線靠近控制點,曲線越陡,遠離控制點,曲線越平
先來一個簡單的例子吧(來個U形):
ctx.moveTo(20,20); ctx.bezierCurveTo(20,100,200,100,200,20); ctx.stroke();
如上圖,第一張是效果圖,第二張是原理圖,曲線從起始點開始,下方有一個控制點,則曲線越陡,到第二個控制點與曲線的切線的點的位置,因為受2個控制點的影響,曲線開始慢慢變平緩,因為2控制點剛好對稱,所以到中間點,曲線出於水平,然後繼續受2個控制點的作用,其中第二個控制點的作用越來越大,知道第一個控制點與曲線的切線點位置,曲線繼續受到第二個控制點的作用,反向受力,到結束點,額,聽不懂,好吧,不懂就不懂吧,記住那句總結性的話就行了!
其經典例子莫過於正弦圖(用2個貝塞爾曲線,一個正U一個反U):
ctx.beginPath(); ctx.moveTo(20,150); ctx.bezierCurveTo(20,50,150,50,150,150); ctx.stroke(); ctx.beginPath(); ctx.moveTo(150,150); ctx.bezierCurveTo(150,250,280,250,280,150); ctx.stroke();
當然,三次貝塞爾曲線不知道能畫U形圖,而是任意曲線都能畫,只有你想不到,沒有畫不了的,哈哈,腦洞打大一下吧!
都說作畫作畫的,不能老是做水墨畫啊,我喜歡顏色,五彩缤紛的顏色,恩,canvas也是可以的,canvas和css3一樣都可以設置漸變,這樣一來,看到彩虹是不是就不遠了,嘻嘻
我們先看看css3的漸變是怎麼設置的,然後對比一下canvas的漸變,我們都知道漸變分為線性漸變和徑向漸變,我們一一來比較:
線性漸變:
.box1{ width:500px; height:50px; background: -webkit-linear-gradient(left, red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%); }
css3可以指定顏色,支持各種顏色格式,且可以指定顏色所在位置,不僅如此,css3還可以指定漸變的方向:
.box1{ width:500px; height:50px; background: -webkit-linear-gradient(45deg , red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%); }
方向可以用角度來定義,45度角是從左下到右上進行漸變
徑向漸變:
.box2{ width:300px; height:200px; background:-webkit-radial-gradient(red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%); }
漸變原點為中心,漸變顏色為百分百之間的顏色漸變
可以看出,徑向漸變是以中心為原點,一圈一圈向外擴散,同樣支持自定義顏色,支持各種顏色格式,支持指定位置,也是可以設置原點和漸變方式(圓形,橢圓):
.box2{ width:300px; height:200px; background:-webkit-radial-gradient(bottom left, ellipse,red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%); }
原點左下,漸變形狀為橢圓
.box2{ width:300px; height:200px; background:-webkit-radial-gradient(bottom left, circle,red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%); }
原點左下,漸變形狀為圓形
以上只是簡單列舉一下css3的漸變樣式,當然css3的漸變肯定不止這些,這裡主要是想對比一下canvas的漸變樣式,順便科普一下!
canvas的漸變相對要簡單一些,沒有那麼多的花花腸子:
createLinearGradient(x1,y1,x2,y2) 創建線性漸變
參數:x1,y1 表示漸變起始點 x2,y2 表示漸變結束點
createRadialGradient(x1,y1,r1,x2,y2,r2) 創建徑向漸變
參數:x1,y1 表示漸變開始圓心坐標,r1表示漸變開始圓的半徑 x2,y2 表示漸變結束圓心坐標,r2表示漸變結束圓的半徑
gradient.addColorStop(stop,color) 規定gradient 對象中的顏色和位置
參數: stop 取值0-1之間,表示漸變中開始與結束之間的位置 color表示漸變顏色
注意,這裡添加漸變顏色的對象並不是context,而是gradient
怎麼用呢?看線性漸變一個小例子:
這個科普一個誤區:
ctx.fillRect(50,50,200,50); var line = ctx.createLinearGradient(50,50,200,50); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)');
這樣是錯誤的,什麼都出不來!
照理說,應該是先創建一個圖像,然後給這個圖形加漸變色,一般的規律都如此,比如畫畫,比如css,但是canvas不一樣,重點來了:canvas凡是設置樣式的,必須放在繪圖前面 ,怎麼理解這句話?
繪圖的方法: fill() , fillRect() , stroke() , strokeRect() , rect()
那設置:比如文字類字體,字體大小,字體顏色,字體陰影,漸變色 路徑類如線條,矩形,圓形,背景,漸變等等
所以正確的格式是:
var line = ctx.createLinearGradient(50,50,200,50); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillRect(50,50,200,50);
可以看到canvas一樣可以自定義顏色,支持各種顏色格式,支持指定位置(用0-1的數,跟百分比類似),相比css3之下,我覺得canvas的漸變顏色區域更加准確!
好了,既然canvas能像css3一樣設置漸變樣式,那可不可以設置方向呢?怎麼設?以上面的代碼為例,我們畫一張圖:
圖可能畫的有點蒙啊,解釋一下,第一張圖表示我們上面的代碼顯示的效果,漸變方向是(50,50)——> (200,50),是一條水平線,表示方向是從左到右漸變,要是我將坐標方向設為(50,50)——> (200,100),如第二張圖,那麼漸變是否是從左上角到右下角呢?我們試一下:
var line = ctx.createLinearGradient(50,50,200,100); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillRect(50,50,200,50);
可以看出角度是正確的,createLinearGradient的2個坐標就是為了指明漸變方向的,那麼canvas的徑向漸變會不會跟css3相同呢?我們寫一個小例子:
var line = ctx.createRadialGradient(150,150,0,150,150,200); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillRect(50,50,200,150);
可以看到,如果圖形即使不是一個正方形,徑向漸變的圓依然是正圓,與css3的漸變機制有點不一樣(css3是橢圓,看上面的css3圖),並且漸變區域是根據兩個圓來決定的,我們改一下這2個圓的區域看看:
var line = ctx.createRadialGradient(150,150,50,150,150,100); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillRect(50,50,200,150);
比對上面的圖可以看出,下圖在50-100的區間裡是有漸變的,其他地方則是首尾的顏色填充,那麼圓心在邊角上呢?
var line = ctx.createRadialGradient(50,50,0,50,50,200); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillRect(50,50,200,150);
效果跟css3的樣式一樣,顏色更加准確,如果2圓的圓心不一樣,會有什麼反應?
var line = ctx.createRadialGradient(50,50,0,150,150,200); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillRect(50,50,200,150);
一個圓心在左上角,一個圓心在中間
咦,什麼鬼,算了,如果想要正常的徑向漸變,還是同圓心吧,不同圓心太詭異,hold 不住啊!
那徑向漸變能像css3一樣可以設置橢圓嗎?咳咳,我只能借用一句台詞:臣妾做不到啊!
我們來一個炫酷的漸變應用:
ctx.font = "40px 微軟雅黑"; var line = ctx.createLinearGradient(10,100,200,100); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.fillText("狂拽炫酷吊炸天",10,100);
下面我們介紹另外一個與顏色有關的屬性——透明!
globalAlpha = num 參數:num取值0-1之間 設置或返回繪圖的當前透明值
有同學會問,按這個詞的意思是全局透明,那麼它是全局的嗎?我也想問,那我們試一下:
ctx.fillStyle = "red"; ctx.fillRect(50,50,100,100); ctx.globalAlpha = 0.5; ctx.fillStyle = "green"; ctx.fillRect(100,100,100,100); ctx.fillStyle = "blue"; ctx.fillRect(150,150,100,100);
可以看到,第一個沒有變透明,後面2個變透明了,那麼如果我將路徑閉合,是否還會出現這樣的效果:
ctx.beginPath(); ctx.fillStyle = "red"; ctx.fillRect(50,50,100,100); ctx.closePath(); ctx.globalAlpha = 0.5; ctx.beginPath(); ctx.fillStyle = "green"; ctx.fillRect(100,100,100,100); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = "blue"; ctx.fillRect(150,150,100,100); ctx.closePath();
結果是一樣的,說明它是”人“如其名啊,果然是全局的,那麼將它放入閉合路徑中,會污染其他的路徑嗎?
ctx.beginPath(); ctx.globalAlpha = 0.5; ctx.fillStyle = "red"; ctx.fillRect(50,50,100,100); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = "green"; ctx.fillRect(100,100,100,100); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = "blue"; ctx.fillRect(150,150,100,100); ctx.closePath();
馬蛋,簡直是嚴重的核污染啊,穿透力如此之強?如果我只想讓第一個透明,後面的不透明?我要怎麼弄呢?
為了解決這個問題,我們需要引入2個方法,同樣是一對活寶啊:
context.save() 保存當前環境的狀態
context.restore() 返回之前保存過的路徑狀態和屬性
怎麼理解這對活寶呢?
可以這麼理解:當設置了save()方法,就相當於將後面的繪圖放在一個堆棧中,與世隔絕,知道看到restore(),就返回到原來的位置,舉個例子哈,就像是一堆糖果,save()就是將一部分糖果裝進盒子,restore()就是封閉盒子,繼續撿糖果,但是盒子裡的糖果就不會與其他糖果混合了,恩,可以這麼理解,特別注意的是,restore()方法必須要有save()才起作用,你想啊,都沒有盒子裝糖果,怎麼能封閉盒子呢
那咱們來看看他們的神奇技能:
ctx.save(); ctx.beginPath(); ctx.globalAlpha = 0.5; ctx.fillStyle = "red"; ctx.fillRect(50,50,100,100); ctx.closePath(); ctx.restore(); ctx.beginPath(); ctx.fillStyle = "green"; ctx.fillRect(100,100,100,100); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = "blue"; ctx.fillRect(150,150,100,100); ctx.closePath();
哎呀,瞬間覺得整個世界都完美了!在面對凶悍的全局變量,屬性或方法時,我們可以用上面的這對活寶來避免它們對我們需要的部分的侵害(這裡為什麼要說侵害,會讓人想污的),確實是好技能!
回到globalAlpha,它的用處還是有很多的,路徑,圖形,文字都可以設置透明,那我們來一個文字透明看看:
ctx.font = "40px 微軟雅黑"; var line = ctx.createLinearGradient(10,100,200,100); line.addColorStop(0,'red'); line.addColorStop(0.2 ,'#0F0'); line.addColorStop(0.5 ,'rgb(51,102,255)'); line.addColorStop(1 ,'rgba(204,255,0,0.8)'); ctx.fillStyle = line; ctx.globalAlpha = 0.3; ctx.fillText("狂拽炫酷吊炸天",10,100);
恩,今天就介紹到這裡吧,後面的內容比較復雜,需要認真的准備,就這樣吧!