原文地址: https://www.jeremyjone.com/490/, 轉載請註明。
橢圓
通過前面的文章,我們已經可以繪製大部分圖形以及文字。但是,在canvas
中,橢圓是一個複雜的存在,本身我們上學時學習橢圓本身也是一個複雜的結構。我看了很多畫橢圓的方案,大部分分爲兩類:
- 第一類是 使用
arc()
畫一個圓形,然後將其縮放變形,完成一個橢圓。 - 第二類是 使用貝塞爾曲線,即使用多條貝塞爾曲線混合拼接爲一個橢圓。
經過我的實際測試,採用我認爲比較簡單並且顯示效果比較好的方式,使用貝塞爾曲線的方式繪製橢圓。
剛纔說過,貝塞爾的方式是使用多條曲線拼接,一個橢圓可以是兩條曲線、三條曲線,或四條甚至更多曲線拼接。我這裏使用兩條就夠了,而且很大程度減少我們的計算量。
關於貝塞爾曲線
貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用於二維圖形應用程序的數學曲線。
在canvas
中,使用bezierCurveTo()
方法來繪製貝塞爾曲線,該方法通過使用表示三次貝塞爾曲線的指定控制點,向當前路徑添加一個點。
- 提示:三次貝塞爾曲線需要三個點。前兩個點是用於三次貝塞爾計算中的控制點,第三個點是曲線的結束點。曲線的開始點是當前路徑中最後一個點。如果路徑不存在,那麼請使用 beginPath() 和 moveTo() 方法來定義開始點。
** 以上摘自HTML5 canvas bezierCurveTo()方法說明
通過貝塞爾曲線繪製橢圓
通過上圖,我們可以清楚看到繪製一條貝塞爾曲線的基本要素。
所以,我們的橢圓應該大致如下(純鼠標繪製,別吐~ ~ ~):
從圖中可以看到,橢圓可以被分成兩條貝塞爾曲線,我們只需要找到對應的點即可。計算過程忽略,直接看代碼(代碼已經經過位置計算的優化,看上去更像使用鼠標點擊的起始點到結束點的橢圓):
let k = ((x2 - x1) / 0.55);
let w = (x2 - x1) / 2;
let h = (y2 - y1) / 2;
// bezier double ellipse algorithm
context.moveTo(x1, y1 + h);
context.bezierCurveTo(x1, y1 + h * 3, x1 + w * 11 / 5, y1 + h * 3, x1 + w * 11 / 5, y1 + h);
context.bezierCurveTo(x1 + w * 11 / 5, y1 - h, x1, y1 - h, x1, y1 + h);
context.stroke();
效果:
這樣就繪製出了一個比較完美的橢圓。
橡皮擦
在canvas中,有三個繪製圖形函數,分別是fillRect()
、strokeRect()
、clearRect()
,分別是填充圖形,空心矩形,和清除矩形,前面兩個函數我們已經使用過。最後的函數通常:
clearRect(0, 0, canvas.width, canvas.height)
可以完成清屏的功能。
我們如果想要達到橡皮擦的功能,該如何操作呢?
其實原理是一樣的,只不過,我們需要先固定住我們需要修改的圖片。
context.arc(x1, y1, 10, 0, 2 * Math.PI); // 畫一個圓形,位置即鼠標當前位置,大小就是橡皮擦的半徑大小,當然,也可以不適用圓形,任意形狀的橡皮擦都可以。
context.clip(); // 這句很重要,它可以將上面一句定義的形狀,從當前畫布中剪切出來單獨操作,如果沒有這一句,那麼一會操作的仍然是整幅畫布。
context.clearRect(0, 0, canvas.width, canvas.height); // 這句很簡單,和剛纔的清空示例是一樣的。
self.X1 = self.X2;
self.Y1 = self.Y2;
所以,它的整體邏輯分三步:
定義橡皮擦的大小和形狀,通常我們定義爲圓形。
固定畫板,將橡皮擦的內容單獨剪切出來。
清空剪切出來的內容,就完成了橡皮功能。
是不是很簡單,當然,需要注意的是,隨後不要忘記和隨心畫一樣,實時修改傳入的座標,達到連續擦出的效果。
這樣,簡單的橡皮功能也完成了。
完整的內容已經更新在了我的github - canvasPaint中,您也可以通過我的示例來測試一下功能。
前期目錄:
JavaScript 之 canvas(一)-- 認識canvas
JavaScript 之 canvas(二)-- 繪製基本圖形
JavaScript 之 canvas(三)-- 使用鼠標實時繪製圖形
JavaScript 之 canvas(四)-- 繪製文字