[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 裡面。
這個核心包含了,
ElementAllocator
RenderNode
EventHandler
方法
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 元件了。