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