Three.js是一個偉大的開源WebGL庫,WebGL允許JavaScript操作GPU,在浏覽器端實現真正意義的3D。但是目前這項技術還處在發展階段,資料極為匮乏,愛好者學習基本要通過Demo源碼和Three.js本身的源碼來學習。
0.簡介 嗨,這是我的第一篇關於如何寫出好的代碼的文章。和很多開發者一樣,我通過實踐學習,但同時我也向其他更有經驗的開發者們學習。在過去的幾個月中,我在canvas標簽上花了很多時間,我想如果把這段時間學到的關於WebGL和JavaScript的小技巧都寫下來,一定很有意思。有一些很具體,有一些卻很籠統,希望你們喜歡!
1.盡快寫一個原型 讓我們從簡單的開始。現在你有個絕妙的注意,那麼你應該盡快就程序裡最復雜的部分寫一個原型,看看這項技術是否可以實現你的想法。WebGL很強大,因為它可以直接操縱顯卡裡的GPU,但是也別忘了你需要通過JavaScript才能訪問顯卡,這比顯卡內部運算的效率可是低多了。事實上,你的天才想法很可能被這種簡單事情擊敗。
2.使用THREE.JS處理3D 就像我的朋友Hakim一樣,我也完全理解我們正使用的技術的底層細節。理解表面之下的東西是很重要的,但是如果你使用three.js,它為你免去了如此多的煩惱。你可以將它用於Canvas,WebGL還有SVG,你也應該找到哪種方式合適你的需求。
3.避免SetInterval 這對所有使用JavaScript創建動畫的人來說,都是很重要的一點。為什麼?假定你設定每20毫秒後執行一次某個函數,而這個函數需要執行超過20毫秒的時間,那麼20ms之後,浏覽器不會在乎,而是直接開始下一次執行。至少你可以使用SetTimeout來設置,在某個函數執行完之後,再次執行它。
事實上,有一個更加新潮卻還是半成品的函數,叫做requestAnimationFrame,它很棒。它很類似於setTimeOut函數,除了在這兩個方面:當標簽頁失去焦點時,它就不再運行了;現在這個函數還是依賴於浏覽器的,標准以後還有可能變化。如果你想要更多的信息,可以訪問Paul Irish的博客。
4.使用倒序循環 這是個不錯的小技巧,可以讓你的循環更快。使用倒序,而且使用while循環。比如,這個循環:
復制代碼 代碼如下:
for(var a = 0; a < arr.length; a++) {
// 做一些什麼
}
它的執行效率不如下面這個循環:
復制代碼 代碼如下:
// 假設數組arr存在
var aLength = arr.length;
while(aLength--) {
// 做一些什麼
}
這可能沒幫你省多少開銷,因為執行的效率主要還是依賴於你在循環體裡面干了什麼。但如果你想程序的不必要開銷減少到最後一個字節,後一個循環肯定贏。
實話說,主要影響程序執行效率的還是數組緩存的長度。你可以(也確實應該)去看看JSPerf去了解這一點,以及其他影響JavaScript性能的因素。
5.使用紋理 在WebGL裡面把物體的任意一個細節都畫出來看上去很誘人,但是,如果有可能的話,你應當注意一下你是否能夠使用紋理,因為它能夠極大地提高性能。在某些特定的情況下,比如陰影或者模糊效果,你也許不得不使用紋理,但在其他時候,你也應該時時關注你是否可以使用紋理。
6.使用緩存 這一點我在自己的試驗力試了很多,在幀循環中,你應當避免引用變量、對象或者其他任何東西。基於這點原因,很值得把你的模型、頂點全部緩存起來,這樣在渲染動畫的時候你就可以快速地訪問到它們。
7.禁用選中 我愛這一小段代碼,我把它放到任何包含Canvas或WebGL的頁面中。
復制代碼 代碼如下:
// 禁用鼠標選中DOM元素
document.onselectstart = function() {
return false;
};
你也可能只想在Canvas控件中禁用選中。這段是我在那些Canvas占據了整個屏幕的項目中使用的代碼。
8.避免在JavaScript中定義CSS 現在,在JavaScript中定義CSS簡直太方便了,尤其是你使用JQuery的時候
復制代碼 代碼如下:
// 盡量不要這樣做
$("#someid").css({
position: 'relative',
height: '30px',
width: '300px',
backgroundColor: '#A020F0'
});
問題是這樣做之後,你的JavaScript代碼中很快就充斥著各種類型的CSS定義,而你同時又使用*.css文件來定義CSS,潛在的問題很難被發現。更好的方法是:使用class模塊化CSS,而且只在JavaScript中定義那些不能預知的CSS類。
9.在對象中定義回調函數 我愛下面這段代碼,這絕不是我自己想出來的,但它是如此整潔美觀。如果你有一大堆回調函數要用,你也許會這樣用的:
復制代碼 代碼如下:
$("#someid").click(function() {
// 回調函數
// 返回false在JQuery中會阻止消息的傳遞和默認行為的放生
return false;
});
或者,你會回調一個在代碼其他地方定義的松散的函數,比如這樣
復制代碼 代碼如下:
$("#someid").click(mySuperFunction);
function mySuperFunction(event) {
// 在這裡做很多事情
return false;
}
這樣做會有一些問題。第一段代碼中,你在某個事件上綁定了匿名函數,你很難將該函數再從事件上解除下來。你當然可以解除某個事件上的所有函數,但你可能在它上面綁定了多個函數,而你只想解除一個。在第二種情況下,你的函數名污染了全局變量空間,代碼的可維護性降低了。所以,考慮這樣做:
復制代碼 代碼如下:
$("#someid").click(callbacks.mySuperFunction);
// 所有的回調函數都在callbacks對象中
var callbacks = {
mySuperFunction:function(event) {
// 更多地工作
return false;
}
}
// 解除某個函數的綁定
$("#someid").unbind('click', callbacks.mySuperFunction);
這樣做整潔又干淨,而且避免了上面提到的兩個問題。
10.鏈式三元運算符 我完全是從Paul Irish的《JQuery,你應該知道的11件事》中學到這個的。這非常好用,你也應該會喜歡。我們經常這樣做:
復制代碼 代碼如下:
// 根據a的值為numberBasedOnA賦值
// 如果a大於5,則賦值200,否則賦值38
var numberBasedOnA = a > 5 ? 200 : 38;
但如果你想這樣做,比如,當值為多少時如何,當值大於多少時如何,當值更加大的時候如何,明白嗎?在這種情況下,鏈式三元運算符非常好用:
復制代碼 代碼如下:
var numberBasedOnA =
a < 5 ? 200 :
a < 7 ? 38 :
a < 11 ? 15 :
a < 15 ? 49 :
64;
// 比這樣做更有效率
// when a >=15