2014/05/26

2013/11/12

Mozart - HTML5 音樂指揮家遊戲

今年我們在 Node Knockout 2013 上面做了一個音樂指揮家的遊戲,可以用手機瀏覽器控制電腦瀏覽器播放音樂的節奏,並且實驗性的支持不同電腦發出不同聲部的音樂,就像交響樂團一樣!下面是我們的 DEMO 影片:



製作這個遊戲用到了以下技術:
  • Device Motion event: 用來偵測手機的加速度,[按這邊]偵測你的手機跟瀏覽器有沒有支援
  • WebSocket: 用來把手機的加速度資訊從手機傳到 node.js server 再傳到電腦端的網頁
  • MIDI.js: 播放 midi檔案,據我所知我們應該進行了大量的 patch XD
  • Audio API: 這邊不是我做的所以不是很清楚,不過就我所知有把 midi 音樂切分成不同聲部並且在不同的電腦播放。
  • Canvas: 在電腦端繪出加速度曲線,並且偵測到超過門檻值記錄時間來找到節拍
 晚點我們整理好後會釋出 source code,請拭目以待 :-)

另外如果覺得我們的點子很有趣,請到 [這個網址] 玩遊戲,並且按下左上角的 [VoteKO] 投給我們一票!


2013/09/03

Customize Your Firefox OS

禮拜六在風雨交加下到了高雄的 KSDG 講了 "Customize Your Firefox OS",從 Firefox OS 介紹、內建客制化能力以及如何透過修改源碼更深度的客制化。台灣有許多 OEM 廠商透過不同的 OS 來建制自己的解決方案,希望這個分享可以讓大家可以瞭解到如何從內建的客制化機制以及修改源碼來建制自己的 Firefox OS。


2013/07/15

Sinon.JS - unit test 斬斷相依性的利器

這兩天跟上周抽出一些時間正在弄 Firefox OS 中 system app 的 unit test,然後這次好好讀了 sinon.js 之後開始試著用他來處理一些相依性問題感覺還真不賴。

是這樣的,unit test 主要的目的是檢測特定的 unit 的工作是否正常運作,在這樣的狀況下我們僅測試該 unit 的邏輯與功能,至於跟它相依的部分通常會在另外一個 unit test 或是 integration test 的時候再測試。

但是問題來了,我想測試的 function 就是有用到其他外部 Object,你總不可能要我整個 Javascript 都沒用到 document.getElementById 吧(這還是有可能啦...)?

Sinon.JS 的其中一個功能就是可以隔絕 unit 對於其他 Object 的相依性,讓開發者可以單獨測試單一 unit 邏輯的好幫手。剛好我最近正在寫 system app 其中 ScreenManager 的 unit test,讓我們來看看 ScreenManager.init() 這個函式的相依性關係:



ScreenManager.init() 除了 call 外部的五個 Object 以外,還調用了自己的三個 function。而這些相依性都可以透過 Sinon 的功能隔絕他們。

在這之前先介紹一下 Sinon.JS 的三大物件:

Spy: 可以把一個 object/function wrap 起來,可以用來監看該 object/function 被呼叫的狀況。舉個例來說,假設我們要測試某個條件下 init() 就會呼叫到 turnScreenOn(), 我們可以用 spy 把 turnScreenOn wrap 起來:

var spyTurnScreenOn = sinon.spy(ScreenManager, 'turnScreenOn');
ScreenManager.init();
assert.isTrue(spyTurnScreenOn.called);

上面這段的意思是如果執行了 init() 之後,ScreenManager.turnScreenOn 會被執行到就代表正確。如果不僅執行到,而且傳入的參數一定要是特定值(比如說 true)也可以這樣用:

spyTurnScreen.calledWith(true);

除了上面提到這兩個 API 以外,還有幾個都還蠻實用的如 calledCount, calledTwice 等等,詳情請見 Spy API

Stub: 有 spy 的所有功能。但是造出 stub 之後,原本的 function 就不會再被呼叫了。我通常都會用 stub 把所有的相依性全部切掉。一樣是 turnScreenOn() 在 init() 會被呼叫的例子,如果使用 stub 代替 spy,不一樣的地方是 spy 還是會去呼叫原有 function,而 stub 則不會呼叫原有 function。

Mock: 有 Spy 跟 Stub 所有功能,但是還可以透過在跑之前設定期望值,最後再檢查 Mock 後的物件是否有照期待的執行。舉個 Sinon.JS 官網上面的例子,下面這樣的寫法可以讓你確認 jquery.ajax 最少被呼叫到了兩次,最多呼叫五次:

sinon.mock(jQuery).expects("ajax").atLeast(2).atMost(5);
...
jQuery.ajax.verify();

跟 Spy, Stub 不一樣的是 Mock 可以在測試還沒開始前就先預先指定期望值,而不是像 Stub 一樣需要等到呼叫後才能檢測條件是否成立。不過我目前測試的部分都可以用 stub 做到,Mock 暫時還沒用到,等到真的有用到之後再跟大家分享。

接下來我們先來看一下相對簡單的例子要怎麼測試:ScreenManager:toggleScreen():

toggleScreen: function scm_toggleScreen() {
  if (this.screenEnabled) {
    // Currently there is no one used toggleScreen, so just set reason as
    // toggle. If it is used by someone in the future, we can rename it.
    this._screenOffBy = 'toggle';
    this.turnScreenOff();
  } else {
    this.turnScreenOn();
  }

這個 function相當的簡單,只要 screenEnabled 是 true,_screenOffBy 就會是 toggle,並且 turnScreenOff 會呼叫到。所以只要利用 sinon.stub 分別作出 turnScreenOff 跟 turnScreenOn 的 stub,並且確認會不會正確的呼叫到即可。

suite('toggleScreen()', function() {
  var stubTurnOff, stubTurnOn;

  setup(function() {
    stubTurnOff = sinon.stub(ScreenManager, 'turnScreenOff');
    stubTurnOn = sinon.stub(ScreenManager, 'turnScreenOn');
  });

  teardown(function() {
    stubTurnOff.restore();
    stubTurnOn.restore();
  });

  test('if screenEnabled is true', function() {
    ScreenManager.screenEnabled = true;
    ScreenManager.toggleScreen();
    assert.equal(ScreenManager._screenOffBy, 'toggle');
    assert.isTrue(stubTurnOff.called);
    assert.isFalse(stubTurnOn.called);
  });

  test('if screenEnabled is false', function() {
    ScreenManager.screenEnabled = false;
    ScreenManager.toggleScreen();
    assert.isTrue(stubTurnOn.called);
    assert.isFalse(stubTurnOff.called);
  });
});

所以我在 5-6 行的地方用 sinon.stub() 分別對兩個 function 做了 stub,這樣一來當 toggleScreen() 呼叫這兩個 function 的時候,就會呼叫到假的 function 了。比如說 15-19 行的測試,當 screenEnabled 是 true 時,turnOff 要被呼叫到,而 turnOn 則不會被呼叫到。sinon.stub() 在這種種狀況就可以協助我們把相依性切除,只專注在測試目標 function 的邏輯。

但是我們的測試環境是將 Firefox OS 在瀏覽器上面運行,這時候有些 Object 很可能是不存在的。比如說 mozTelephony,所以就不能用上面 sinon.stub(Object, propertyName) 的方式造假物件,還是要利用 sinon.stub() 造一個新的物件。正是因為這樣我設計了 switchProperty/restoreProperty 的 helper function 用來替換掉可能不存在的物件。

再來看看一些比較進階的例子:如果你有一個 function 只希望在某種狀況下,才使用 stub 指定的回傳值,而其他狀況則是調用原本的 function 該怎麼寫呢?init() 的 setup() 正好有這樣的狀況:

var stubById = sinon.stub(document, 'getElementById').withArgs('screen')
    .returns(document.createElement('div'));

這段的意思是我們為 document.getElementById 做了一個假物件,他會直接回傳一個 div DOM 元件,但是只有在傳入參數為 screen 的時候才會發生,所以說:

document.getElementById('screen');

這個時候就會直接回傳一個假的 div,而不會真的到 DOM 裡面查詢一個 #screen 的元素,而且如果你查的是任何其他的參數,就還是可以正常運作!這樣可以確保你的 stub 只在你想要的參數被觸發!

還有一個很常需要對付的狀況:如果你的測項是需要被 callback 呼叫才能測該怎麼辦呢?舉例:你用了一個 addEventListener('click', callback),所以說一定要 click 才能呼叫 callback 該怎麼辦呢?用下面的 function:

sinon.stub(window, 'addEventListener').callsArgWith(1, evt)

如果一呼叫 addEventListener 就會直接呼叫第一個參數('click' 是 0, callback 是 1),這樣就可以測試更深層的狀況了。

總而言之,Sinon.JS 準備了一拖拉庫的造假工具給你用,Javascript Unit Test 有了像 Sinon.JS 這樣強大的工具,就可以切斷相依性,測試的更徹底!

另外這邊有一個 Firefox OS 裡面的範例:screen_manager.jsscreen_manager_test.js,有興趣的可以參考參考。

2013/07/04

Gaia Development Workflow

這是 Firefox OS - Gaia 開發時的 workflow,沒時間寫文章,先放張圖。


2013/06/30

Running Firefox OS Gaia on Windows


你永遠不知道下一個要解的 Bug 是什麼 :-)

上週送了一個 Pull Request 到 Gaia,Reviewer 非常好心的跟我說我的 patch 在 Windows 上面不會動,這時候才第二次意識到我們還是需要在 Windows 上面測試(上一次是我修 customization 的時候遇到的)。總之我這次很老實地把 Windows 的環境搞定了,在我等待 fetch pull request 的時候就來說說怎麼設定吧。

首先下載 Firefox Nightly

接著你需要 MinGW,寫這篇文章的時候我裝的是 mingw-get-inst-20120426.exe,或許你看到這篇文章的時候已經有新版出來了。安裝的時候記得要選下面這兩個:
  • MSYS Basic System
  • MinGW Developer ToolKit(這個我不確定要不要)
接下來安裝 git,Git 直接到官方網站下載就好了。安裝的時候選擇 "Run Git from the Windows Command Prompt",這樣可以直接在 MinGW 的環境使用 git。

然後接下來是裝 Python,一樣到官網下載就好了。 但是你需要把 Pyhton 的路徑加入 PATH 這個環境變數裡面,在 My Computer 按右鍵,選 properties -> Advanced -> Environment Variables,找到 Path 把 C:\Python27 加入。

最後的一個步驟,安裝一些 build gaia 會用到的指令:

$ mingw-get install msys-wget
$ mingw-get install msys-zip
$ mingw-get install msys-unzip

這樣就搞定啦!

打開 MinGW Shell 找個風水吉地 git clone gaia,進入目錄後用下面指令 build 出 profile 檔:

$ DEBUG=1 make

第一次會需要久一點,主要是下載一些 xulrunner 之類的東西。好了之後用下面的指令啓動你的 Nightly。依據你 clone Gaia 的地方會有些不一樣,自己摸索一下吧。

$ /C/Program\ Files/Nightly/firefox.exe -profile /C/MinGW/msys/1.0/home/IEUser/gaia/profile-debug/

這樣就可以在 Windows 的 Nightly 上面啓動 Gaia 囉,在裡面收信也沒問題喔 LOL


2013/06/25

關於辦 COSCUP 的那些美好回憶

下面是一個可口可樂的廣告,幾年前我看到這個廣告的時候就很感動,先跟大家分享一下。




不知道這個 Blog 的觀眾多少人辦過研討會?從 2005 年準備要跟 ICOS 分成兩個研討會開始我就陸陸續續地參加 COSCUP 的工作人員,從聽眾、場務、講者、主持人到最近連續四年擔任記錄組與議程組成員。

今年我擔任了議程組長。

我記得在去年慶功宴的時候,我跟 Bob 說,『Hey, 議程組我混熟了,明年我來當議程組長吧』我壓根沒想到過了一季之後,我們決定要在 TICC 台北國際會議中心辦 COSCUP。

更沒想到的是半年後,我們不只要在 TICC 辦,我們準備籌辦八軌議程。

親愛的觀眾,八軌議程阿,這可不是鬧著玩的。

兩年前我擔任紀錄組長,在約八百人規模,四軌議程下我都累得跟狗一樣了。 然而今年 COSCUP 就像是個小型社會,高度壓力,密集溝通,工作追蹤。當然工作人員之間臉紅脖子粗的爭吵也少不了。我再度的累得跟狗一樣,我想要跟金凱瑞在電影『一個頭兩個大』一樣,自己賞自己一拳,然後再給自己一個過肩摔。

我對著躺在地上的那個 Loser 說:嘿你看吧,你這個魯蛇,你自找的。我就像是躺在大雨裡的泥濘,雨滴落在地上的聲音就像是那些邊拍手邊笑彎了腰的圍觀人群嘲笑我。

在這種夜裡,我常常會想起這個廣告的片段。

『大部份的人會告訴你,你選擇了一個最糟糕的時刻。 ... 我們正經歷一場危機,這不是什麼好事』

白天的工作已經很忙了,晚上回家繼續默默地回著根本不可能看完的信件。

『可是這會讓你更堅強』

讓我想到 Bob 在某年的慶功宴舉杯的畫面

敬你。

這也是很艱難的一年啊,那年我們把報名系統弄炸了(笑

你說我還記得多少有多辛苦?我心裡只記得當所有結束之後,那些快樂,震撼還有為了結束的那一點惆悵。

今年我完成了我從來沒想過的目標,以及從未想過的困難。來自那些 1,500 人入場的壓力,來自八軌議程的壓力,還有截稿前兩週只有三十個人投稿的壓力。

這些背在肩膀上的事情讓人咬牙的覺得痛苦。

但是這些終究會過去的。

當拉開布幕後,當我們像螞蟻一樣在會場穿梭後,當大家在舉杯致敬後, 我很確定那些所有的辛苦都會在落幕之後煙消雲散。而我們現在唯一要做的,就是為了拉開序幕的那刻做好準備。

今年夏天你打算做什麼呢?

我打算辦一個台灣最大的開放源碼研討會 ;-)


BTW, [工商服務] 個人贊助活動仍在招募中,歡迎個人贊助喲。



2013/01/22

威能的 Firefox OS unit testing

今天在謀智台客發表了篇文章,主要是講 Firefox OS 在 unit testing 有個不錯的機制,就是設定妥當後,當你在任何編輯器或 IDE 按下儲存後,unit testing 就會自動開始測試跟你剛剛儲存的那個 javascript 相關的測項,最後用 Mac 的 notification center 或是 Linux 的 libnotify 告訴你測試結果,像下面這樣:



可以讓你隨時都知道自己有沒有把任何東西搞爆了。

有興趣的可以看一下

缺它不可!靈活運用 Firefox OS Gaia 的單元測試

2013/01/02

travis + jsdom: Javascript unit test 的最後一塊拼圖

在昨天晚上出去跨年前,我把自己之前練習寫的 weather app 重新整理過了一次,主要是把存取 DOM 的部分聚集起來然後寫 unit test 以及開始用 travis 測試。

travis 是好幾個朋友跟我提過但是我都沒認真看怎麼玩,終於在上次的 Hacking Thursdaykanruczchen 討論了幾番之後決定還是自己來試試看比較有感覺。

travis 提供的服務就如 Jenkins 一樣,是一個 CI (continuous integration) 的服務 -- 但是不用自己架 Jenkins 對我來講實在太棒了,我是真的很怕麻煩還要自己維護 CI service。Travis 是直接跟 github 整合的 CI service,所有在 github 的專案都可以很簡單地用 travis hook 透過一個 .travis.yml 來指定 build 以及測試的方式。

jsdom 則是一個在 node.js 上面的 DOM 實作,意思就是說可以利用它在 node.js 裡面操作 window, document 等元件。這個對我來說真是大大的福音。因為 mobile-weather 大多數的邏輯操作都是跟 DOM 相關的,如果剔除這些其實也沒什麼邏輯好測試的。jsdom 讓我可以在 node.js 裡面操作 DOM 等於我就可以驗證一些跟 DOM 相關的邏輯。

jsdom 真的是 javascript unit test 的最後一塊拼圖,有他之後我們就可以測試跟 DOM相關的部分了 :D

最後我在 unit test 採用的是 mocha, chai,前者是 node.js 上面的 unit test framework,後者是 assertion library。

首先第一個步驟是寫 package.json。因為我們的 unit test 會跑在 node.js 裡面,所以我們要先寫 package.json 來交代要用到哪些 library,並且我們可以在裡面指定當執行 "npm test" 的時候要如何執行測試。



4 - 8 行的部分是指定 devDependencies 要使用 mocha, chai 跟 jsdom。9 - 11 行做的是執行 npm test 的時候事實上會執行 node_modules/.bin/mocha -u tdd test/weather_test.js。這之後如果換了 unit test framework 也可以很輕易地從這邊換掉測試方式 :-)

至於 mocha -u tdd 是 mocha 同時有提供 BDD 跟 TDD 的 interface 可以置換,我這邊是採用 TDD。

接下來就可以開始來寫 unit test 了。

目前的 unit test 測試的項目還不多,先把架構弄好之後再慢慢補上。第三行是把 index.html 轉成字串儲存下來。在 7 - 12 行的地方利用 jsdom 生成 window 跟 document,並且每次都重新生成 Weather object 跟重新初始化,主要的目的是讓每個 test case 不會互相影響。test case 我們挑在 20-25 行的 'updateWeekday' 來看。我們知道 2013/1/1 是禮拜二,而 updateWeekday 裡面就會把 day0.textContent 設定為 'T' (Tuesday),接下來依序設定。


所以我們就可以假定 day0.textContent 是 T, day4.textContent 是 S 這樣的方式來測試。寫完 test cast,下達 npm test 就可以進行測試囉。

這樣 unit test 就告一個段落,最後就是把它移到 travis 上面就大功告成了!先在根目錄開個 .travis.yml,內容是你的 travis 組態:

這邊我們指定採用 node.js,並且只測 0.8 這個版本。其實我們只有在 unit test 的時候會用到 node.js,基本上他還是一個一般的 browser app。所以不需要測試多個 node.js 版本。把這邊全部都 push 上 github 後,用你的 github 帳號登入 travis。然後在 account 的頁面找到你的 project 把右邊的 switch 推到 on。

當下次你 commit & push 的時候,就會看到 travis 開始測試你的 project 囉!

並且有每一次的 build log:


超棒的吧,不用在自己架 Jenkins 囉!新年快樂!

目前的計劃是如果我們 fork jsdom 然後把 Firefox OS 有用到的 API 加進去,我們就可以開心的丟掉 Browser 測試 Firefox OS app 了 :D

2012/11/27

[Firefox OS] HTML 內嵌 SVG 動畫實例

前言:這些實作都已經包含在最新的 Firefox OS 裡面,有興趣的可以 check 最新的 code 出來玩。

最近接到了一個需要變更設計的 issue,我看到這個設計愣了一下,主要的原因是因為要做出這樣的設計用 HTML + CSS 還真的需要想一下如何實作。

主要更改的地方是 Firefox OS 的兩個元件:Lockscreen 跟 Dialer。

這兩個元件主要的設計概念都是希望有一條像是橡皮筋(或跳繩)的線上下的跳動,並且在往上跳動的時候露出下面的兩個按鈕,提示使用者可以把這條橡皮筋往上滑,接下來按下按鈕解鎖。我們這邊只討論如何實作,不討論視覺設計 :P



如上圖所示,上面的那條弧線是需要動態的上下彈動,如果用 HTML + CSS 的話有幾種方法可以嘗試做到跟缺點:
  1. 上一個 canvas,然後把這條線在 canvas 上面不斷的重繪(缺點:這樣要不停的計算跟重繪)
  2. 用超級多張 png 不停的置換圖檔(缺點:要生出超級多圖)
  3. 先畫一張弧線,接著用 CSS 的 Transform 更改他的 scale()(缺點,接近中間的時候整個圖形就會被壓得很扁)
考慮過 HTML + CSS 的解法之後,以上的解法似乎都不太好。這個時候我就開始考慮用 SVG 來做這件事情。但是用 SVG 來做這件事情其實是蠻冒險的,因為在這期間問了幾個同事他們都沒有測試過 SVG 在 Firefox OS 真正的手機上的詳細效能。所以收到這個設計的時候我先寫信問了個對於整個 platform 比較熟的同事,然後因為 deadline 非常的趕,但是我又不能不確定效能狀況就下手,所以就先決定做個獨立可以同時在電腦跟手機都可以驗證的小型 app。

這個 app 要驗證的事情有兩件:
  1. 當用滑鼠(手指)按住拖曳,這個時候改變 SVG 的屬性讓他改變弧度的效能衝擊有多大
  2. 使用 SVG animation (SMIL) 效能到底如何
然而做這個 DEMO 幾乎也可以搞清楚要怎麼用 SVG 實作這個解鎖畫面了。在弧線的部分,採用 SVG 的 Path 搭配上 c (curveto) 參數可以達成弧線,動畫的部分則是用 SMIL 的 animate tag 完成。下面這個網頁就是驗證效能用的網頁(我只用過 Firefox 打開過,其他瀏覽器不知道有沒有支援):

http://yurenju.github.com/lockscreen-demo/wrapper.html

這邊只是用來驗證的網頁,所以會有一些小 bug。主要的功能就是往上拉的時候用 javascript 去改變 SVG d (data) 裡面的 c (curveto) 的參數,放開滑鼠的時候用 SMIL animate 把 curve 滑回原位。而下面有個連結 install lockscreen demo 用途是如果你用 Firefox OS 的手機點了這個連結就可以把這個 demo app 安裝到手機裡面。

很棒的是當我把這個 app 安裝到手機裡面,發現這樣實作的效能在手機上是完全可以接受的!既然可以接受那就大膽的把這樣的實作方式引入 Firefox OS。這邊有針對 lockscreen 的 commit


在 SVG 方面,首先利用 path tag 來劃出最原始的弧線。在 attribute d (data) 裡面用了兩個參數:M (moveto) 跟 C (curveto),moveto 用來指定 path 的起點,curveto 用來指定用來控制曲線的兩根桿子的弧度。SVG 1.1 Path 裡面有張圖讓 curve 的控制點比較好理解一點:



C 後面接的兩組 (x, y) 分別是兩個控制點的坐標,我們需要的大概像是最左上角那張圖的效果。attribute 'd' 裡面的最後一個參數則是曲線的終點。整個 attribute 長這樣:

M0,80 C100,150 220,150 320,80

第一個是曲線起始坐標,最後一個是曲線結束坐標,中間兩個則是控制點的坐標。

接下來說明 path 裡面的五個 animate tag。1 跟 3 是針對曲線的彎度變化,而 2 跟 4 則是針對透明度的變化。1-4 都是用於拖曳橡皮筋之後放開的動畫,而 (5) 則是當你不去碰橡皮筋時,他的彈跳提示動畫。請想像這一整組動畫:一條繩子彈上去後隨著重力掉下來,掉到地上之後會再彈跳幾下後靜止。下面講解比較複雜的第五組動畫:

先看到 values。用分號切分開來的話總共有五組數據:
  1. 起始的曲線數據 (Y=150)
  2. 第一次彈跳到最高的數據,兩個控制點的 Y 坐標都變少了讓整個圓弧的開口朝下 (Y=40)
  3. 回到最地上 (Y=150)
  4. 再次彈起來,但是幅度較低 (Y=100)
  5. 回到原點 (Y=150)
至於 keySplines 則是指定彈跳的 timing function,如果你用過 CSS animation,就跟 ease-in/out 那種差不多的東西,只是要直接指定數據,下面這張圖是 keySplines 設定 0.5 0 0.5 1 會產生的 timing function:


keySplines 裡面有四組數據,分別就是 1-2, 2-3, 3-4, 4-5 這四段動畫的 timing function。每一組數據裡面都是兩個控制點的坐標。

SVG 的部分大概就是這樣!這部分有很多需要細微調整的,有興趣的就留言一起討論吧。接下來是 Javascript 部分。這邊我就講一些 SVG + Javascript 要注意的小技巧
  • fill=freeze 功能為讓動畫結束之後停留在最後一格,不過這樣的話如果你想要用 mousemove 去逐漸改變曲線的外形時,你會發現這個屬性會讓整個 path 卡死。如果拿掉 fill=freeze 的話,因為我執行完動畫之後還要把曲線固定在最後一格,所以拿掉的話就導致動畫有閃爍的現象。 解法就是平常不用,等到要播放補間動畫的時候再把 freeze 加上去。
  • 用 beginElement(), endElement() 來播放、停止動畫
  • addEventListener endEvent 來處理動畫結束後的後續處理。
這邊的細節真的非常的多,如果你也想 HTML + SVG + Javascript 來實作的話,建議是要讀一下 SVG 跟 SMIL 的 spec,然後撿想要用的東西放在裡面,什麼不明白的事情就直接寫到 SVG 裡面看一下效果如何就是了。那個時候這個 commit 要上真是超級緊張的,因為這是我開始 contribute Firefox OS 以來最大的修改。結果上的時候還是有些小細節沒注意到,感謝同事的幫忙在 bug 還沒關之前就注意到這個低級錯誤然後讓我可以及時的推入 repository 了。

之後更複雜的是 Dialer 的部分。



如上圖所見左右兩邊各有一個會隨著弧線移動的兩個 spotlight,而線段上的顏色還多了紅色跟綠色線段。線段不同顏色方面,看遍了 SVG 的資料後比較方便的方法還是用多重的漸層並且把兩個漸層的 offset 設定成一樣,這樣就可以讓曲線有不同的顏色。至於隨著 curve 的 spotlight 則是透過 clipPath 作修剪遮罩,讓漸層只在部分的地方露出來即可。做完這次的 commit 我的 SVG 功力真的大增啊... Orz



這是在 Dialer 曲線用的漸層。2 跟 3 的 offset 都是一樣的,但是顏色卻用不一樣,這樣的技巧可以讓線段不會產生漸層。

這段是如何產生 spotlight 的方法。首先 path 不一樣的地方是 d 除了原本的 M 跟 C 以外,又加了 H V Z 分別用來畫出橫線、直線跟關閉 path 用。fill style 則套用上面的 #gradient-red 的漸層紅色,最後用 clipPath 的方式作剪裁遮罩。對綠色的部分也用相同的方法,最後通通拿去做動畫,就完成啦!

所以這是最後的結果:



Dialer 最後實作的結果在這邊,很可惜速度上並不是很好,目前看起來撥電話進來之後整隻手機的效率會下降許多,目前我們也正在改進這個問題。



這個 lockscreen 的 code 都在 github 上面,有興趣的可以抓下來玩玩 :-)