/ Famo.us

[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]
[0, 0.5] [0.5, 0.5]
[0, 1] [0.5, 1]

相關模組

相同於核心的 SurfaceModifier 這兩個模組,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,既然有 pipesubscribe 可用,所以就嘗試了一下,後來官方也回信來,用法確實是需要使用 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);
});

當中有提及 subscribepipe 在此暫且不詳細說明,之後提及 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 上面同步刊出。