DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> HTML基礎知識 >> HTML5詳解 >> html5 Canvas畫圖教程(11)—使用lineTo/arc/bezierCurveTo畫橢圓形
html5 Canvas畫圖教程(11)—使用lineTo/arc/bezierCurveTo畫橢圓形
編輯:HTML5詳解     
在canvas中可以很方便的用arc方法畫出圓形,本來圓形也可以看作是一個寬高相等的橢圓,但canvas中根本沒有畫橢圓的方法,我們要用其他方法來模擬。 
我們首先要明確畫一個橢圓需要那些參數,基本的幾何知識告訴我們,橢圓需要圓心坐標,寬度,高度——或者還有旋轉角度,不過這個可以暫時不要,旋轉是比較容易的。 
1,使用lineTo畫橢圓 
你沒有看錯,lineTo這樣一個純粹用來畫直線的方法居然可以用來畫橢圓!?但他確實存在,不過寫法實在是有些不可思議: 

復制代碼代碼如下:www.mb5u.com
function DrawEllipse(Canvas,O,OA,OB){ 
//畫橢圓,例子:var B=new Array(150,150); DrawEllipse(hb,B,50,30); 
with (Canvas){ 
var x=O[0]+OA; 
var y=O[1]; 
moveTo(x,y); 
for (var i=0;i<=360;i++){ 
var ii=i*Math.PI/180; 
var x=O[0]+OA*Math.cos(ii); 
var y=O[1]-OB*Math.sin(ii); 
lineTo(x,y); 




這個方法的原理是,一個圓有360度,那麼就用lineTo循環360次,畫出每一度的線段,最終連成一個橢圓。其中需要用到三角函數正弦余弦進行計算。 
注意,這個方法的第2個參數是個數組,即橢圓的圓心坐標. 

思路很奇葩,而且畫出的橢圓也比較平滑。但不值得大家使用——此方法每畫一個橢圓,就要循環360次,只有畫的橢圓稍微一多,對浏覽器的性能就是個考驗。 
我們只用了解一下他的思路即可 
2,使用arc畫圓,然後把他縮放成一個橢圓 
這個方法的原文在此,核心函數如下: 

復制代碼代碼如下:www.mb5u.com
var canvas = document.getElementById('myCanvas'); 
var context = canvas.getContext('2d'); 
var centerX = 0; 
var centerY = 0; 
var radius = 50; 
// save state 
context.save(); 
// translate context 
context.translate(canvas.width / 2, canvas.height / 2); 
// scale context horizontally 
context.scale(2, 1); 
// draw circle which will be stretched into an oval 
context.beginPath(); 
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false); 
// restore to original state 
context.restore() 

此方法用了一個我前面還沒講過的canvas函數,即scale,他能實現canvas的縮放。縮放有水平和垂直兩個方向,代碼中把canvas水平方向放大了,而垂直方向不變,so,原來arc畫出的圓形就變成了一個橢圓。 
這個方法初看甚妙,代碼少,而且原理淺顯易懂。但分析一下就能發現他的明顯缺點了,就是——不精確!比如我需要寬171高56的橢圓,此時我們如果把arc的半徑定為28的話,那麼後面就要為171/28/2這個蛋疼的不知所雲的數字郁悶了。 

不過有個折中的辦法是始終把arc的半徑設成100,然後,不夠就放大,超過了就縮小。但是,還是不精確。 
3,使用貝賽爾曲線bezIErCurveTo 
自從覺得上面的縮放法不精確後,我就很想找到一個精確的畫橢圓的方法,最後在stackoverflow上找到了: 

復制代碼代碼如下:www.mb5u.com
function drawEllipse(ctx, x, y, w, h) { 
var kappa = 0.5522848; 
ox = (w / 2) * kappa, // control point offset horizontal 
oy = (h / 2) * kappa, // control point offset vertical 
xe = x + w, // x-end 
ye = y + h, // y-end 
xm = x + w / 2, // x-middle 
ym = y + h / 2; // y-middle 
ctx.beginPath(); 
ctx.moveTo(x, ym); 
ctx.bezIErCurveTo(x, ym - oy, xm - ox, y, xm, y); 
ctx.bezIErCurveTo(xm + ox, y, xe, ym - oy, xe, ym); 
ctx.bezIErCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); 
ctx.bezIErCurveTo(xm - ox, ye, x, ym + oy, x, ym); 
ctx.closePath(); 
ctx.stroke(); 


這個方法可以算是比較完美的了。他把一個橢圓分成了4條貝塞爾曲線,用他們連成了一個橢圓。最後寬度高度也比較精確,開銷也較少。 
但此方法依然有缺點。大家看那個kappa參數,有個很奇特的值,相信很多人在幾何專家告訴你為什麼他要取這個值之前,都不明白為什麼非要取這個值——我到現在還是不知道。並且我有很強烈的想把他改一下看看有什麼後果的沖動。 

當然我這種類似強迫症患者的沖動並不能說成是此方法的缺點,他真正的缺點是——為什麼要用4條貝塞爾曲線?我個人覺得,一個橢圓明顯是由兩條貝塞爾曲線組成的而不是4條。這個想法最終讓我找到了最完美的畫橢圓的方法。
 4,使用兩條貝賽爾曲線畫出橢圓 
為了了解上一個方法能否精簡,我專門注冊了一個stackoverflow的帳號去提問,由於問題裡有圖片,積分不夠不能傳,我還迫不得已用勉勉強強的英語水平去回答老外的問題掙積分。但最終好運到了,回答一個問題就解決了我的積分問題。 
我提的貝賽爾曲線和橢圓的關系的問題在此. 
說實話,下面老外的回答我大部分沒看懂,但幸虧他提供了一個代碼示例頁,讓我明白了原理,在此對他表示再次的感謝。而根據他的解答,我找到的畫橢圓的方法如下: 

復制代碼代碼如下:www.mb5u.com
//橢圓 
CanvasRenderingContext2D.prototype.oval = function (x, y, width, height) { 
var k = (width/0.75)/2, 
w = width/2, 
h = height/2; 
this.beginPath(); 
this.moveTo(x, y-h); 
this.bezIErCurveTo(x+k, y-h, x+k, y+h, x, y+h); 
this.bezIErCurveTo(x-k, y+h, x-k, y-h, x, y-h); 
this.closePath(); 
return this; 


此方法既精確,又代碼少,而且也沒有奇怪的難懂的地方。只需要記住這一點,橢圓的寬度與畫出橢圓的貝賽爾曲線的控制點的坐標比例如下: 
貝塞爾控制點x=(橢圓寬度/0.75)/2這一點已經在代碼中體現了。 

大家可自行試驗上面的4個方法畫出橢圓。 
如果你發現了更簡單的方法,也請分享出來大家探討吧。
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved