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

現(xiàn)在可以看到我們已經(jīng)大致繪制出一棵樹了。
圖片
不過還少了樹的果實。
繪制果實
繪制果實很簡單,只需要在繪制樹枝結(jié)束的時候,去把果實繪制出來就行,其實果實就是一個個的白色實心圓:
圖片
至此這棵樹完整繪制完畢。
圖片
繪制部分的代碼如下:















                        
					    
 
 
 





 
 
 
 