溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用lineTo/arc/bezierCurveTo畫橢圓形

發(fā)布時間:2022-03-24 10:59:05 來源:億速云 閱讀:310 作者:iii 欄目:web開發(fā)

這篇“如何使用lineTo/arc/bezierCurveTo畫橢圓形”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“如何使用lineTo/arc/bezierCurveTo畫橢圓形”文章吧。

在canvas中可以很方便的用arc方法畫出圓形,本來圓形也可以看作是一個寬高相等的橢圓,但canvas中根本沒有畫橢圓的方法,我們要用其他方法來模擬。

我們首先要明確畫一個橢圓需要那些參數(shù),基本的幾何知識告訴我們,橢圓需要圓心坐標(biāo),寬度,高度——或者還有旋轉(zhuǎn)角度,不過這個可以暫時不要,旋轉(zhuǎn)是比較容易的。

1,使用lineTo畫橢圓

你沒有看錯,lineTo這樣一個純粹用來畫直線的方法居然可以用來畫橢圓?。康_實(shí)存在,不過寫法實(shí)在是有些不可思議:

復(fù)制代碼 代碼如下:

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循環(huán)360次,畫出每一度的線段,最終連成一個橢圓。其中需要用到三角函數(shù)正弦余弦進(jìn)行計算。

注意,這個方法的第2個參數(shù)是個數(shù)組,即橢圓的圓心坐標(biāo).

思路很奇葩,而且畫出的橢圓也比較平滑。但不值得大家使用&mdash;&mdash;此方法每畫一個橢圓,就要循環(huán)360次,只有畫的橢圓稍微一多,對瀏覽器的性能就是個考驗(yàn)。

我們只用了解一下他的思路即可

2,使用arc畫圓,然后把他縮放成一個橢圓

這個方法的原文在此,核心函數(shù)如下:

復(fù)制代碼 代碼如下:

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函數(shù),即scale,他能實(shí)現(xiàn)canvas的縮放??s放有水平和垂直兩個方向,代碼中把canvas水平方向放大了,而垂直方向不變,so,原來arc畫出的圓形就變成了一個橢圓。

這個方法初看甚妙,代碼少,而且原理淺顯易懂。但分析一下就能發(fā)現(xiàn)他的明顯缺點(diǎn)了,就是&mdash;&mdash;不精確!比如我需要寬171高56的橢圓,此時我們?nèi)绻補(bǔ)rc的半徑定為28的話,那么后面就要為171/28/2這個蛋疼的不知所云的數(shù)字郁悶了。

不過有個折中的辦法是始終把a(bǔ)rc的半徑設(shè)成100,然后,不夠就放大,超過了就縮小。但是,還是不精確。

3,使用貝賽爾曲線bezierCurveTo

自從覺得上面的縮放法不精確后,我就很想找到一個精確的畫橢圓的方法,最后在stackoverflow上找到了:

復(fù)制代碼 代碼如下:

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條貝塞爾曲線,用他們連成了一個橢圓。最后寬度高度也比較精確,開銷也較少。

但此方法依然有缺點(diǎn)。大家看那個kappa參數(shù),有個很奇特的值,相信很多人在幾何專家告訴你為什么他要取這個值之前,都不明白為什么非要取這個值&mdash;&mdash;我到現(xiàn)在還是不知道。并且我有很強(qiáng)烈的想把他改一下看看有什么后果的沖動。

當(dāng)然我這種類似強(qiáng)迫癥患者的沖動并不能說成是此方法的缺點(diǎn),他真正的缺點(diǎn)是&mdash;&mdash;為什么要用4條貝塞爾曲線?我個人覺得,一個橢圓明顯是由兩條貝塞爾曲線組成的而不是4條。這個想法最終讓我找到了最完美的畫橢圓的方法。

 4,使用兩條貝賽爾曲線畫出橢圓

為了了解上一個方法能否精簡,我專門注冊了一個stackoverflow的帳號去提問,由于問題里有圖片,積分不夠不能傳,我還迫不得已用勉勉強(qiáng)強(qiáng)的英語水平去回答老外的問題掙積分。但最終好運(yùn)到了,回答一個問題就解決了我的積分問題。

我提的貝賽爾曲線和橢圓的關(guān)系的問題在此.

說實(shí)話,下面老外的回答我大部分沒看懂,但幸虧他提供了一個代碼示例頁,讓我明白了原理,在此對他表示再次的感謝。而根據(jù)他的解答,我找到的畫橢圓的方法如下:

復(fù)制代碼 代碼如下:

//橢圓

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;

}

此方法既精確,又代碼少,而且也沒有奇怪的難懂的地方。只需要記住這一點(diǎn),橢圓的寬度與畫出橢圓的貝賽爾曲線的控制點(diǎn)的坐標(biāo)比例如下:

貝塞爾控制點(diǎn)x=(橢圓寬度/0.75)/2這一點(diǎn)已經(jīng)在代碼中體現(xiàn)了。

以上就是關(guān)于“如何使用lineTo/arc/bezierCurveTo畫橢圓形”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI