將前兩天畫好的樹枝骨幹,搭配第二章學的動畫效果,就能讓樹開始擺動了:
https://jerry-the-potato.github.io/Chapter3-demo-object/
當然,由於參數ab設置過頭,滑鼠移動到邊邊角角時,整棵樹變得很奇怪(我看像是稻穗!不,是棋盤!)
看完之後,是不是有發現卡卡的呢?(如果沒有可能是你的繪圖處理器太給力了XD)
原因就出在昨天賣的一個關子:「單純用如此簡單的物件結構,會導致過多重複的代碼」
用Edge瀏覽器可以看到,JS佔用了相當大的容量和CPU:
已經會導致掉偵的程度了,真的是很母湯呢!
把結構修改後:
改動的地方非常少:
Class把原本的建構式拆出來寫,結構上可以較清楚在實例化的當下會建構哪些變數
其實不是物件不好用,而是還沒有跟大家說prototype的概念,這個下禮拜會有兩個篇幅的故事來解釋,今天就簡單用現實生活比喻,糖尿病會遺傳這個是大家都知道的,但是我現在又沒有糖尿病,怎麼知道未來有沒有機會得呢?醫生會去看我的家族史,往上追本溯源,看有沒有人得過糖尿病。
在這個案例中,我遺傳(繼承)了我家族的疾病,而為了評估得病的風險,就要去看生我的人(爸爸的prototype),父輩祖輩一直往上,因此,只要我家族有人得過遺傳病,接下來好幾代的子孫都有得病風險。
以程式的觀點來說,就是當我要把整個家族的病歷給歸檔的時候,或是上帝要決定誰會得病(虛擬世界!?),最麻煩的方式就是給每個個體做標記,一出生的時候就寫說,這個人有10%、那個人有20%,如果是這樣,光是一個糖尿病就要給每個人都貼過一次標籤,要設定無數個變數。反過來講,最簡單的方式,就是只標記那個得糖尿病的爺爺,這樣,雖然我沒辦法直接知道每個個體的得病機率,但是可以透過他們跟爺爺的血緣關係(繼承鍊),來得知結果,這樣一來就只需要在爺爺身上設定一個變數,就可以解決問題了。
觀念說清楚了,就可以實作:
效能也更好了
歸根結柢也是因為給每節樹枝設的變數太多,等到時候開始做遊戲再來簡化
因為有不少變因,像是:
像是滑鼠點擊時產生新的樹,但是舊的樹還沒有觸發JS的自動回收機制,假設一直連點滑鼠,JS堆積大小會來到20MB,過一陣子後,才會看到降回5MB左右,這是由於myTree這個變數不再指向那些舊的樹,被JS自動回收機制認為是垃圾,才清除掉,這期間存在一個緩衝期。
以及如果只是重新整理頁面會有殘留的JS堆積,所以以上的圖片數據皆不準!實際上實測的結果並沒有什麼差異。
不過時間已經不夠再深入研究了ww,日後再回來修改,繼續我們的畫樹之旅吧!
這邊我們用最懶的方法,直接讓透明度漸變,使樹幹到樹枝末端呈現透明度1~0.5的漸變:
let alpha = 0.5 + 0.5 * (r / (window.innerHeight/3));
context.strokeStyle = 'rgba(179, 198, 213, ' + alpha + ')';
為了看起來不那麼突兀,要讓樹從一個原點生出,當初原點是設置在(WIDTH/2, HEIGHT),因此減去這個位置,取得相對座標,接著設置一個this.grow參數,讓樹再建立之初以0.1開始,慢慢增加,若每次增加0.01會顯得平鋪直敘太無聊,因此模仿一下緩衝函式,讓this.grow成長到一定程度後,就開始變慢:
let x = (this.startX - WIDTH / 2) * this.grow + WIDTH / 2,
y = (this.startY - HEIGHT) * this.grow + HEIGHT,
r = this.r * this.grow,
this.grow = Math.min(this.grow + 0.01 / (0.8 + 2 * this.grow), 1);
Tree.prototype.Draw = function () {
let x = (this.startX - WIDTH / 2) * this.grow + WIDTH / 2,
y = (this.startY - HEIGHT) * this.grow + HEIGHT,
r = this.r * this.grow,
theta = this.theta;
context.beginPath();
context.moveTo(x, y);
context.lineTo(x + r * Math.cos(theta / 180 * Math.PI),
y + r * Math.sin(theta / 180 * Math.PI));
context.lineWidth = 1 + r / 50;
let alpha = 0.5 + 0.5 * (r / (window.innerHeight/3));
context.strokeStyle = 'rgba(179, 198, 213, ' + alpha + ')';
context.stroke();
// 如果有子樹枝,就繼續呼叫所有的子樹枝
if (this.son) this.son.forEach(branch => branch.Draw()); // 遞迴
this.grow = Math.min(this.grow + 0.01 / (0.8 + 2 * this.grow), 1);
};
可以敲碗效能議題的部分嗎XD
剛好我昨天晚上發文後在看尾遞迴、迭代和Stack,今天就實作了XD,不是非常專業,麻煩鞭小力一點。
https://ithelp.ithome.com.tw/articles/10272091