[UPDATE] Famo.us 官方已捨棄此模組,詳情請看官方 Github
這三樣東西是組成 Famo.us 引擎的主要功臣,負責 rFA 與 DOM 的溝通橋樑。
Engine 核心
在 Famo.us 當中,這個核心做的事情其實並不會太多,大抵上就是這些,
- 設定 rFA
- 監聽
resize - 強迫取消
touchmove事件(使用 preventDefault() - 使用
EventHandler監聽事件 - 使用
OptionsManager設定預設值,並且監聽其change來更新元件
Engine 方法
pipe,unpipe,on,emit對於EventHandler相關模組的操作getFPS取得目前 Engine 的 FPSsetFPSCap設定 FPS 上限setOptions設定 Engine 的屬性createContext建立一個 div 的 Engine 框架,所有元件都必須被add在這個框架裡面。這個框架是採用核心模組Context來建立,所以會回傳Context這個元件。registerContext用來註冊一個Context元件,並且在下一次 loop 的時候出現在畫面上nextTick傳入下一個 loop 所需要執行的 function(它只會執行一次defer跟nextTick很類似,但是是延遲執行(如果每次運行時間小於MAX_DEFER_FRAME_TIME的話,預設是 10(單位秒
Engine 監聽事件
prerender在做nextTick與defer之前會先 emmitpostrender做完nextTick與defer之後才會 emmit
FPS
預設的 Engine 所使用的 FPS 是 1000 / frameTime,而這個 frameTime 的來由,是經過每次 loop 的執行時間去相減(單位是秒,也就是說,
frameTime = 目前時間 - 上一次運行的時間戳記
所以 Engine 並不是真的運行在 60 fps 底下的,而是依照你的瀏覽器執行時間來決定目前的 FPS 該是多少。而,setFPSCap 則是在這個取得 frameTime 的方法之前,做一個限制,
// skip frame if we're over our framerate cap
if (frameTimeLimit && currentTime - lastTime < frameTimeLimit) return;
也就是說,如果你設定 Engine.setFPSCap( 60 ); 就代表這個 frameTimeLimit 是 1000 / 60,如果運行的 loop 小於這個時間(速度過快,那麼這一次的 loop 就不會被執行了。
使用範例
以下是 Engine.createContext, Engine.registerContext 操作範例,
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Context = require('famous/core/Context');
var Surface = require('famous/core/Surface');
var elem = document.createElement('div');
elem.style.backgroundColor = 'black';
elem.style.width = '100px';
elem.style.height = '100px';
document.body.appendChild(elem);
var context = new Context(elem);
Engine.registerContext( context );
var mainCtx = Engine.createContext();
var surface = new Surface({
size: [100, 100],
properties: {
backgroundColor: 'red'
}
});
mainCtx.add(surface);
});
輸出結果在畫面上,你可以看瀏覽器所產生的 DOM 會有兩個 <div>,大概會長這樣,
<div style="background-color: black; width: 100px; height: 100px;"></div>
<div class="famous-container">
<div style="transform-origin: 0% 0% 0px; opacity: 0.999999; z-index: 0; transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); background-color: black; width: 100px; height: 100px;" class="famous-surface"></div>
</div>
這樣應該可以理解 Engine.createContext 與 Engine.registerContext 的差別。Engine 核心中,最主要的就是 Context 核心處理 DOM 的部分。而處理 DOM 的部分則是 ElementAllocator 來負責,這裡就分別說明。
ElementAllocator 核心
這個核心專門在處理 DOM 中的元素,當你初始化這個物件的時候,預設就會拿到 document.createDocumentFragment() 來當作初始物件(或者是,你也可以傳入一個 DOM 的物件給他
但是,由於這個物件的方法,在官方說明下都是屬於私有方法,單獨要拿出來用也不是不行。只是用法有別。主要這個物件,是用來儲存 DOM 的物件,
allocate用於儲存 DOM 物件deallocate用於取出儲存的 DOM 物件migrate把原本所屬的 DOM 子元件,轉移到新的 DOM 父元件底下getNodeCount計算所屬的子元件數量
底下的例子就會在 document.body 底下加入一個空的 <div> 元素。
var body = new ElementAllocator(document.getElementsByTagName('body')[0]);
body.allocate('div');
不過既然他是私有物件,就不再多贅述了。
Context 核心
這個核心主要是將 RenderNode 打包成物件並且將它放到 DOM 裡面。
這個核心包含了,
ElementAllocatorRenderNodeEventHandler
方法
add在這個物件內加入一個 renderable 物件migrate與ElementAllocator類似,將這個物件搬移到另一個物件下getSize取得該物件的寬度與高度setSize設定該物件的寬度與高度,如果沒有傳入值,就是document的尺寸getPerspective取得該物件的透視深度(單位 pxsetPerspective設定物件透視深度,允許三個傳入值,分別是perspective,transition與callback,其中transition必須要是Transitionable物件on監聽事件emit觸發事件removeListener移除監聽事件pipe,unpipe將自身事件指定(或取消)到其他物件上
使用方式
這一個元件必須要傳入一個 DOM 的元件,舉個簡單的例子來看,
var elem = document.createElement('div');
elem.style.backgroundColor = 'black';
elem.style.width = '100px';
elem.style.height = '100px';
document.body.appendChild(elem);
var ctx = new Context(elem);
console.log(ctx);
這個 ctx 物件就可以用來控制 DOM 元件了。