離譜!產(chǎn)品要求我用 JavaScript 畫一顆【隨機(jī)樹】!
用 JavaScript 畫一棵樹?
產(chǎn)品說要讓前端用 JavaScript 畫一棵樹出來,但是這難道不能直接讓 UI 給一張圖片嗎?
圖片
后來一問才知道,產(chǎn)品要的是一顆隨機(jī)樹,也就是樹的茂盛程度、長度、枝干粗細(xì)都是隨機(jī)的,那這確實(shí)沒辦法叫 UI 給圖,畢竟 UI 不可能給我 10000 張樹的圖片吧?
Canvas 畫一顆隨機(jī)樹
接下來使用 Canvas 去畫這棵隨機(jī)樹。
基礎(chǔ)頁面
我們需要在頁面上寫一個(gè) canvas 標(biāo)簽,并設(shè)置好寬高,同時(shí)需要獲取它的 Dom 節(jié)點(diǎn)、繪制上下文,以便后續(xù)的繪制。
圖片
坐標(biāo)調(diào)整
默認(rèn)的 Canvas 坐標(biāo)系是這樣的。
圖片
但是我們現(xiàn)在需要從中間去向上去畫一棵樹,所以坐標(biāo)得調(diào)整成這樣:
- X 軸從最上面移動(dòng)到最下面。
- Y 軸的方向由往下調(diào)整成往上,并且從最左邊移動(dòng)到畫布中間。
圖片
這些操作可以使用 Canvas 的方法:
- ctx.translate: 坐標(biāo)系移動(dòng)。
- ctx.scale: 坐標(biāo)系縮放。
圖片
繪制一棵樹的要素
繪制一棵樹的要素是什么呢?其實(shí)就是樹枝和果實(shí),但是其實(shí)樹枝才是第一要素,那么樹枝又有哪些要素呢?無非就這幾個(gè)點(diǎn):
- 起始點(diǎn)
- 樹枝長度、樹枝粗細(xì)
- 生長角度
- 終點(diǎn)
開始繪制
所以我們可以寫一個(gè) drawBranch 來進(jìn)行繪制,并且初始調(diào)用肯定是繪制樹干,樹干的參數(shù)如下:
- 起始點(diǎn):(0, 0)
- 樹枝長度、樹枝粗細(xì):這些可以自己自定義
- 生長角度:90度
- 終點(diǎn):需要算
圖片
這個(gè)終點(diǎn)應(yīng)該怎么算呢?其實(shí)很簡單,根據(jù)樹枝長度、生長角度就可以算出來了,這是初高中的知識(shí)
圖片
于是我們可以使用 Canvas 的繪制方法,去繪制線段,其實(shí)樹枝就是一個(gè)一個(gè)的線段:
圖片
到現(xiàn)在我繪制出了一個(gè)樹干出來:
圖片
但是我們是想讓這棵樹開枝散葉,所以需要繼續(xù)遞歸繼續(xù)去繪制更多的樹枝出來。
遞歸繪制
其實(shí)往哪開枝散葉呢?無非就是往左或者往右。
圖片
所以需要遞歸畫左邊和右邊的樹枝,并且子樹枝肯定要比父樹枝更短、比父樹枝更細(xì),比如我們可以定義一個(gè)比例:
- 子樹枝是父樹枝長度的 0.8。
- 子樹枝是父樹枝粗細(xì)的 0.75。
而子樹枝的生長角度,其實(shí)可以隨機(jī),我們可以在 0° - 30° 之間隨機(jī)選一個(gè)角度,于是增加了遞歸調(diào)用的代碼:
圖片
但是這個(gè)時(shí)候會(huì)發(fā)現(xiàn),報(bào)錯(cuò)了,爆棧了,因?yàn)槲覀冎贿f歸開始,但卻沒有在某個(gè)時(shí)刻遞歸停止。
圖片
我們可以自己定義一個(gè)停止規(guī)則(規(guī)則可以自己定義,這會(huì)決定你這棵樹的茂盛程度):
- 粗細(xì)小于 2 時(shí)馬上停止
- (粗細(xì)小于 10 時(shí) + 隨機(jī)數(shù))決定是否停止
現(xiàn)在可以看到我們已經(jīng)大致繪制出一棵樹了。
圖片
不過還少了樹的果實(shí)。
繪制果實(shí)
繪制果實(shí)很簡單,只需要在繪制樹枝結(jié)束的時(shí)候,去把果實(shí)繪制出來就行,其實(shí)果實(shí)就是一個(gè)個(gè)的白色實(shí)心圓:
圖片
至此這棵樹完整繪制完畢。
圖片
繪制部分的代碼如下: