[Famo.us] 安裝與 Surface, Modifier 基本操作

[UPDATE] Famo.us 官方已捨棄此模組,詳情請看官方 Github

Famo.us 原始碼可以在官方的 github 下載


如果你已經可以進入 Famo.us University,那這裡或許可以跳過。

基本使用

Famo.us 是基於 AMD 的架構來撰寫,且搭配使用 require.js,所以稍微了解一下這兩個東西會比較容易上手。

安裝

你可以從官方的 github 把專案抓下來直接使用,也可以用 nodeJS 來安裝,這裡介紹一下 nodeJS 的安裝方法,或者是參考官方的 Install

sudo npm install -g yo grunt-cli bower generator-famous
mkdir newProject && cd newProject
yo famous

     _-----_
    |       |
    |--(o)--|   .--------------------------.
   `---------´  |    Welcome to Yeoman,    |
    ( _´U`_ )   |   ladies and gentlemen!  |
    /___A___\   '__________________________'
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

You're using the fantastic Famo.us generator.
[?] Your project name: (newProject)   // 輸入專案的名稱(不打也行
[?] Your project description: e.g. Seed project to get started with Famo.us   // 輸入專案的描述(不打也行
[?] Would you mind telling me your username on Github?  // 輸入你的 github 名稱(不打也行

跑了一陣子之後,他會把需要的東西全部安裝完畢,最後用 grunt serve 來啟動服務。他會啟動瀏覽器連線到 localhost:1337,同時支援 livereload,所以你可以直接修改 js 檔案來看到效果。

檔案會放在 app/src/main.js 裡面。

顯示元件

建構一個畫面,我們需要 Engine, Surface 這兩個模組,所以,這樣就能夠在畫面上產生一段文字

define(function(require, export, module) {
  var Engine = require('famo/core/Engine');
  var Surface = require('fame/core/Surface');

  var mainContext = Engine.createContext();

  var surf = new Surface({
    // 這個 size 代表這個 Surface 是 200x200 的正方形
    size: [200, 200],
    content: "Hello World"
  });

  mainContext.add(surf);
});

然後,我們可以利用 Modifier 這個模組,來修改或是製作這個 Surface 的效果

define(function(require, exports, module) {
  var Engine = require('famous/core/Engine');
  var Surface = require('famous/core/Surface');
  var Modifier = require('famous/core/Modifier');
  var Transform = require('famous/core/Transform');

  var mainContext = Engine.createContext();

  var surf = new Surface({
    // 這個 size 代表這個 Surface 是 200x200 的正方形
    size: [200, 200],
    content: "Hello World"
  });

  var mod = new Modifier({
      // transform 表示套用一組 Tramsform 模組的效果
      transform: Transform.translate(100, 0, 0),
      // origin 表示把底下的元素放在螢幕某個位置,0.5, 0.5 代表正中央
      origin: [0.5, 0.5]
  });

  mainContext.add(mod).add(surf);
});

Surface 核心模組

它是屬於核心模組之一,原始碼在 core/Surface.js 可以查看。預設所使用的 Markup 是 <div> 元素,預設的類別樣式名稱為 famous-surface,這兩項是無法被修改的。可以傳入的屬性有,

  • properties 用來設定 CSS 樣式
  • content 傳入的內容,允許使用 HTML
  • classes 傳入類別樣式名稱,必須使用陣列
  • size 傳入元件的尺寸,陣列,規格為 [ width, height ],必須單純為數字(單位 px,比較特別的是,他可以傳入 undefined 這個關鍵字,代表意義是 100%

Modifier 核心模組

核心之一,原始碼在 core/Modifier.js 可以查看。它可以當作是一種效果的套用(或是下層元素的濾鏡)來看,樹狀結構以上面的例子來看,會長成這樣

mainContent
    |
 Modifier
    |
 Surface

這一個 Modifier 並不會產生任何的元素,相反的,它會是一種 CSS Style 屬性,_套用_在 Surface 的元素上面,以 HTML Mackup 來解釋就是這樣,

<div class="famous-surface" style="/* Modifier 會在這裡發生 */"></div>

它可以傳入的屬性有,

  • transform 這個傳入值必須要是 Transform 模組
  • opacity 這個傳入值是透明度,範圍在 0 ~ 1
  • origin 這是代表我要將所屬元素設定在畫面的哪個位置,它是一個陣列 [ 垂直位置, 水平位置 ]
  • size 這個尺寸跟 Surface 的尺寸設定相同,但是會覆蓋掉 Surface 所設定的尺寸

origin 是一種九宮格的形式,所代表的相對位置如下表,

| 左 | 中 | 右 |
--- | ---- | ---- | ---- |
上 | [0, 0] | [0.5, 0] | [1, 0] |
中 | [0, 0.5] | [0.5, 0.5] | [1, 0.5] |
下 | [0, 1] | [0.5, 1] | [1, 1] |

相關模組

相同於核心的 Surface 與 Modifier 這兩個模組,Famous 對他有個別擴展兩組模組,分別是,

Surface 模組

  • surfaces/CanvasSurface
  • surfaces/ContainerSurface
  • surfaces/ImageSurface
  • surfaces/InputSurface
  • surfaces/VideoSurface

以上這幾種模組都是由 core/Surface 擴展而來,唯一的差異性是這些模組所使用的 HTML Tag 不盡相同,

模組 使用的 HTML Tag 可傳入屬性設定
Surface <div> 同上述 Surface 部分
CanvasSurface <canvas> canvasSize 為一陣列 [ width, height ]
ContainerSurface <div> 同 Surface
ImageSurface <img> content 必須為圖片的 URL
InputSurface <input> placeholder, value, type, name,同時 content 是無效的
VideoSurface <video> 同 Surface 但是多了 autoplay 是布林值

在此必須把 ContainerSurface 特別提出來說明,他與 Surface 雷同,但是在 HTML Markup 的結構上並不太一樣,簡單來說,它可以把很多的 Surface 給包(Group)起來,舉例來說,

define(function(require, exports, module) {
  var Engine = require('famous/core/Engine');
  var Surface = require('famous/core/Surface');
  var Modifier = require('famous/core/Modifier');
  var Transform = require('famous/core/Transform');
  var ContainerSurface = require('famous/surfaces/ContainerSurface');

  var mainContainer = Engine.createContext();
  var container = new ContainerSurface();

  var surface, modifier;
  for (var i = 0; i < 5; i++) {
    surface = new Surface({
      size: [100, 100],
      content: 'box '+ i
    });

    modifier = new Modifier({
        transform: Transform.translate( i * 110, 0, 0 )
    });

    container.add(modifier).add(surface);
  }

  mainContainer.add(container);
});

這樣的方式所產出的 HTML Markup 會類似這樣(底下結構已簡化,

<div class="famous-surface">
  <div class="famous-group famous-container-group">
    <div class="famous-surface">test</div>
    <div class="famous-surface">test</div>
    <div class="famous-surface">test</div>
    <div class="famous-surface">test</div>
    <div class="famous-surface">test</div>
  </div>
</div>

這就是 ContainerSurface 主要的功能,將你的子元素包成一包,然後把這一包放到 DOM 裡面(或是另外給其他的模組使用。

Modifier 模組

  • modifiers/Draggable
  • modifiers/Fader
  • modifiers/ModifierChain
  • modifiers/StateModifier

首先先提一下 StateModifier 這個模組跟 Modifier 幾乎一樣,只是 Modifier 做的事情比 StateModifier 更多一些,所以其實可以把它看成是簡化版本的 Modifier。它只能更動原有的屬性設定,如,size, origin, opacity, transform 這四種而已。

Fader 則更輕巧,他所能做的事情只有將你的元素淡入或是淡出(預設是隱藏的

ModifierChain 則是可以把多種 Modifier 串起來,這樣你就不用一直使用 .add() 來新增了。

Draggable 目前可利用狀況不明,你可以套用,但是做不出任何效果,我已經寫信去問官方,有回覆會再來這邊更新。

在等待官方回覆的同時,我依照 EventHandler 的特性來思考這個 Modifier,既然有 pipe 與 subscribe 可用,所以就嘗試了一下,後來官方也回信來,用法確實是需要使用 pipe 或是 subscribe,實際範例如下,

define(function(require, exports, module) {
  var Engine = require('famous/core/Engine');
  var Surface = require('famous/core/Surface');
  var Modifier = require('famous/core/Modifier');
  var Transform = require('famous/core/Transform');
  var Draggable = require('famous/modifiers/Draggable');
  var ContainerSurface = require('famous/surfaces/ContainerSurface');

  var mainContext = Engine.createContext();

  var container = new ContainerSurface();

  var surface, modifier, drag;
  for (var i = 0; i < 5; i++) {
    surface = new Surface({
      size: [100, 100],
      content: 'box '+ i,
      properties: {
        backgroundColor: "red"
      }
    });

    modifier = new Modifier({
        transform: Transform.translate( i * 110, 0, 0 )
    });

    drag = new Draggable();

    // 使用 subscribe
    drag.subscribe(surface);
    // 或是使用 pipe
    // surface.pipe(drag);

    container.add(drag).add(modifier).add(surface);
  }

  mainContext.add(container);
});

當中有提及 subscribe 與 pipe 在此暫且不詳細說明,之後提及 EventHandler 的時候會再詳細說明。

Surface 新增模組

  • surfaces/FromContainerSurafce
  • surfaces/SubmitInputSurface
  • surfaces/TextareaSurface

大概就如同字面上的意思,跟 surfaces/InputSurface 類似,主要是表單與其元件的模組。使用的方式也跟 surfaces/InputSurface 雷同。

補充說明 ModifierChain

補充說明 ModifierChain 的使用方式,

define(function(require, exports, module) {
  var Engine = require('famous/core/Engine');
  var Surface = require('famous/core/Surface');
  var Modifier = require('famous/core/Modifier');
  var ModifierChain = require('famous/modifiers/ModifierChain');
  var Transform = require('famous/core/Transform');

  var mainContext = Engine.createContext();

  var surface= new Surface({
    size: [100, 100],
    properties: {
      backgroundColor: "red"
    }
  });

  var modifierChain = new ModifierChain(
    new Modifier({
      origin: [0.5, 0.5],
      transform: Transform.rotateZ( 45 * Math.PI / 180 )
    }),
    new Modifier({
      transform: Transform.translate(100, 100, 0)
    }),
    new Modifier({
      size: [200, 200]
    }),
  );

  mainContext.add(modifierChain).add(surface);
});

小結

本文在我的 famous.tw 上面同步刊出。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei