canvas高階 如何畫出平滑的曲線

2022-07-08 23:30:20 字數 3044 閱讀 5543

嗯,相信這對canvas使用較熟的童鞋來說僅僅只是幾十行**就可以搞掂的事情,以下demo就是乙個再也簡單不過的例子了:

它的實現邏輯也很簡單:

我們在canvas畫布上主要監聽了三個事件:mousedownmouseupmousemove,同時我們也建立了乙個isdown變數;

當使用者按下滑鼠(mousedown,即起筆)時將isdown置為true,而放下滑鼠(mouseup)的時候將它置為false,這樣做的好處就是可以判斷使用者當前是否處於繪畫狀態;

通過mousemove事件不斷採集滑鼠經過的座標點,當且僅當isdowntrue(即處於書寫狀態)時將當前的點通過canvas的lineto方法與前面的點進行連線、繪製;

通過以上幾個步驟我們就可以實現基本的畫板功能了,然而事情並沒那麼簡單,仔細的童鞋也許會發現乙個很嚴重的問題——通過這種方式畫出來的線條存在鋸齒,不夠平滑,而且你畫得越快,折線感越強。表現如下圖所示:

為什麼會這樣呢?

出現該現象的原因主要是:

要畫出平滑的曲線,其實也是有方法的,lineto靠不住那我們可以採用canvas的另乙個繪圖api——quadraticcurveto,它用於繪製二次貝塞爾曲線。

二次貝塞爾曲線

quadraticcurveto(cp1x, cp1y, x, y)

呼叫quadraticcurveto方法需要四個引數,cp1xcp1y描述的是控制點,而xy則是曲線的終點:

更多詳細的資訊可移步mdn

既然要使用貝塞爾曲線,很顯然我們的資料是不夠用的,要完整描述乙個二次貝塞爾曲線,我們需要:起始點、控制點和終點,這些資料怎麼來呢?

有乙個很巧妙的演算法可以幫助我們獲取這些資訊

獲取二次貝塞爾關鍵點的演算法

這個演算法並不難理解,這裡我直接舉例子吧:

假設我們在一次繪畫中共採集到6個滑鼠座標,分別是a, b, c, d, e, f

取前面的a, b, c三點,計算出bc的中點b1,以a為起點,b為控制點,b1為終點,利用quadraticcurveto繪製一條二次貝塞爾曲線線段;

接下來,計算得出cd點的中點c1,以b1為起點、c為控制點、c1為終點繼續繪製曲線;

依次類推不斷繪製下去,當到最後乙個點f時,則以de的中點d1為起點,以e為控制點,f為終點結束貝塞爾曲線。

ok,演算法就是這樣,那我們基於該演算法再對現有**進行一次公升級改造:

let isdown = false

;let points =;

let beginpoint = null

;const canvas = document.queryselector('#canvas');

const ctx = canvas.getcontext('2d');

//設定線條顏色

ctx.strokestyle = 'red';

ctx.linewidth = 1;

ctx.linejoin = 'round';

ctx.linecap = 'round';

canvas.addeventlistener('mousedown', down, false

);canvas.addeventlistener('mousemove', move, false

);canvas.addeventlistener('mouseup', up, false

);canvas.addeventlistener('mouseout', up, false

);function

down(evt) =getpos(evt);

points.push();

beginpoint =;

}function

move(evt) =getpos(evt);

points.push();

if (points.length > 3)

drawline(beginpoint, controlpoint, endpoint);

beginpoint =endpoint;

}}function

up(evt) =getpos(evt);

points.push();

if (points.length > 3)

beginpoint = null

; isdown = false

; points =;

}function

getpos(evt)

}function

drawline(beginpoint, controlpoint, endpoint)

在原有的基礎上,我們建立了乙個變數points用於儲存之前mousemove事件中滑鼠經過的點,根據該演算法可知要繪製二次貝塞爾曲線起碼需要3個點以上,因此我們只有在points中的點數大於3時才開始繪製。接下來的處理就跟該演算法一毛一樣了,這裡不再贅述。

**更新後我們的曲線也變得平滑了許多,如下圖所示:

使用一組點畫出平滑的曲線

今天在專案中需要人臉上的點來勾勒出人臉的輪廓,我的想法是將要畫的點存入乙個陣列,使用了uibezierpath來連線每乙個點。但是這樣畫出來的圖是折線,顯得過於生硬。查了若干資料,後來終於在stackoverflow上找到了乙個很好的解決辦法。這個做法的原理其實是在每兩個點之間加入一些點,來使得兩個...

櫻花的季節,教大家用CANVAS畫出飛舞的櫻花樹

又到了櫻花的季節,教大家使用canvas畫出飛舞的櫻花樹效果。廢話少說,先看效果。檢視演示效果 第一步,我們先畫出一棵樹的主體。我畫樹的使用的原理是,定義乙個起始點,從這個點開始,向乙個角度移動一段距離。得到另乙個點。畫出一條線連線兩個點。以新得到的點,依舊向這個角度,移動一段距離。得到第三個點,連...

櫻花的季節,教大家用CANVAS畫出飛舞的櫻花樹

又到了櫻花的季節,教大家使用canvas畫出飛舞的櫻花樹效果。廢話少說,先看效果。檢視演示效果 第一步,我們先畫出一棵樹的主體。我畫樹的使用的原理是,定義乙個起始點,從這個點開始,向乙個角度移動一段距離。得到另乙個點。畫出一條線連線兩個點。以新得到的點,依舊向這個角度,移動一段距離。得到第三個點,連...