2012年6月26日 星期二

[新案上線] TOYOTA 86


Toyota 86 (強列建議使用Chrome或Safari觀看…。)
http://www.toyota.com.tw/cars/86


 第一次完整獨立完成一個純js的商業案。
最近為了這個案子傷了不少腦袋。
這是一個html的網站,目標是要能在所有的裝置及瀏覽器上觀看,所以flash馬上被排除,但是又希望能做到像flash一樣精彩的效果,乍看之下其實沒什麼,就是播播影片而已嘛。
剛開始我參與會議時心裡也是這麼想的,應該沒什麼。

不過真正執行後就開始遇到一些問題了。

首先是影片的問題,整個網站的幾個主要大單元是由影片串場的,因為原本的需求是希望隨時能在影片中的某一個場景停下來(甚至是倒播),又希望整個串場個過程能夠順暢,因此評估之後我們採用了大量的連續圖檔來處理這一塊。
為了要讓影片播放順利,所以需要預先載入一定量的圖檔,這跟影片的buffer是一樣的道理,只不過在載入的過程中我發現電腦的記憶體使用量大量的爆增,直到整台電腦的記憶體都被吃光之後,載入的動作就會發生錯誤,這時即使再釋放記憶體,再重新載入新的圖片也會失敗,原因不明。
所以要在何時適放部份的記憶以及何時開始播放影片、何時載入新的圖檔,變成了這個案子最花工的地方…。
不過也因為這樣無意中發現了Chrome及Safari在記憶體的配置處理上真的比FF及IE厲害許多。

但礙於實在有太多圖檔無法適放,因此在部份電腦的FF及IE下,還是會發生記憶體不足的情況,因此只能說這仍然是個不完整的案子…。

另外一個問題就是手持裝置的效能問題。

原本我的寫法是用兩個div去輪流播放當下該出現的圖片,為什麼要用兩個?其中一個要墊在下面,先放入下一張圖,這樣才不會造成閃爍,這個概念好像叫做double buffer。
不過這樣的話,吃cpu似乎吃得還挺重的,在手機上(我個人是使用HTC Sensation)整個畫面跑起來非常吃力,一點都不流暢。
所以後來就在上線前我馬上改寫成canvas,但為了讓IE系列仍然能正常觀看,所以IE仍然是使用div來播放。
我想現階段這方面的效能也只能這樣調整了,至少部份的手機上跑起來雖沒有很快速但也可以接受。

再來就是音效的問題。

音效原本我是使用HTML5的Audio來處理,但是FF不吃mp3,只吃wav,這下子不得了,光背景音樂wav就要5mb,所以最後折衷,唯獨音效的部份使用了flash,放棄了行動裝置上的聲音效果。

開發心得大概是這樣,其它很多是比較細節的(解析度的調整、js的物件技巧、從外部load單元進來呈現… 等等)就不再額外提了。
但是真的是從這案子吸收到不少經驗。

2012年5月8日 星期二

IE下Facebook JS SDK getLoginStatus無法取得登入狀態



這又是一篇不走正途的解法…大概寫一下就好。

之前在執行專案時發現,在一部份的IE裡(有些ok,有些不ok),即使使用者已經授權且登入的狀態下,Facebook JS SDK的getLoginStatus仍然一直無法取得登入資訊,所以導致這些IE的使用者完全無法使用Facebook JS SDK。

搞了很久,一直沒辦法解決這個問題。
後來火了,想說反正所有的api都是透過access_token做認證的,只要我能取得access_token的話,就應該能夠繼續使用api。

要取得access_token的方式很簡單,用導向的方式處理就可以了,文件說明在這:https://developers.facebook.com/docs/authentication/client-side/
Client-side authentication without the JS SDK的部份,很簡單。
因此我在頁面上加入判斷,針對這些不ok的IE,我先將他們導向到
https://www.facebook.com/dialog/oauth?client_id="+YOUR_APP_ID+"&redirect_uri="+YOUR_REDIRECT_URI+"&scope="+COMMA_SEPARATED_LIST_OF_PERMISSION_NAMES+"&response_type=token
其中的參數:
YOUR_APP_ID = api id
YOUR_REDIRECT_URI = 使用者授權後要導回來的url,通常就是原本的網站本身
COMMA_SEPARATED_LIST_OF_PERMISSION_NAMES = 要取得的授權

導回來後,access_token就會跟在網址後面一起回來了,格式會是像這樣:
http://xxx/#access_token=USER_ACCESS_TOKEN&expires_in=NUMBER_OF_SECONDS_UNTIL_TOKEN_EXPIRES
於是access_token就取得了。

接下來要面臨的問題就是,access_token我們是取得了,但是Facebook JS SDK仍然無法work,因為它還是不知道access_token,所以我們要做的事情就是把access_token塞給Facebook JS SDK,強迫它使用這一組access_token。
Facebook JS SDK在使用者登入後會把一些資訊存在FB._authResponse這一個物件裡,包括access_token,如果使用者還沒登入的情況下FB._authResponse會是一個null值,但沒關係,我們可以自動幫它new出來,然後再把access_token塞進去。
if (!FB._authResponse) FB._authResponse = {};
FB._authResponse.accessToken = 我們自己取得的access_token;

這樣一來就可以直接使用原本的Facebook JS SDK了。
測試過後是沒問題的,甚至我把其它人的access_token塞進去也可以跑出正確的資料出來,因此可以很肯定Facebook JS SDK的確是依靠FB._authResponse這個物件來判斷當下的使用者身份。

希望這一篇會幫助到同樣受IE所苦的開發者們。

2012年2月23日 星期四

既然Flash版GoogleMap不再發新的key…



總覺得自己老是在走偏門…=_=。

自從GoogleMap Flash版停止再發送key值之後,GoogleMap的Flash版本等於正式走入歷史,也因此我們要在flash裡使用GoogleMap的話會變得很頭大。
雖然說以後全flash的網站「理論上」會越來越少,應該都會是flash搭配html一起邁向美好的未來,但是有時難免客戶(或設計師)為了整體視覺著想(或其它因素),一定要在flash使用GoogleMap的話,那就是我們要苦往肚子裡吞的時候了。
這兩天研究了一下,想說看有沒有辦法弄一個簡單的GoogleMap到flash裡,結果似乎是可行的。
說穿了,原理其實就是計算經緯度,然後跟Google「偷偷」的拿取該經緯度下的圖片,再呈現在flash裡而已。

GoogleMap的圖片區塊
可以先參考一下這一篇:
http://code.google.com/intl/zh-TW/apis/maps/documentation/javascript/v2/overlays.html#Google_Maps_Coordinates
要取得圖片的網址很簡單,瞄一下httpWatch的話就會知道,格式類似這樣:
http://mt0.googleapis.com/vt?lyrs=m@170000000&src=apiv3&hl=zh-TW&x=1&y=1&z=15&s=G&style=api|smartmaps
Google把地圖切分成很多區塊,每一個區塊(Tile)的圖片大小是256X256,在不同級數之下會有不同數量區塊,數量的增加是以2的次方在增加的。

也就是說,在縮放等級(zoom)是0的情況之下,世界地圖只有2的0次方=1張的圖片,zoom=2的時候,x軸及y軸各有2的2次方=4張圖,一共16張圖。
有了這個概念的時候,我們就可以依照zoom的大小,配上實際經緯度投射到平面座標上的點,來找出目前該經緯度所對應到的圖片,然後再從Google那邊把圖抓回來就對了,剛剛上面的圖片網址裡,x及y就是代表該區塊的index值,z則是縮放值。

經緯度的投射座標
既然已經知道地圖被切成多少區塊,就很方便取得了,但麻煩的在於經緯度。
GoogleMap使用的是「麥卡托投影法」來計算經緯度,請參考wiki:
http://zh.wikipedia.org/wiki/%E9%BA%A5%E5%8D%A1%E6%89%98%E6%8A%95%E5%BD%B1
這種投影法將地球投影成圓柱狀體,也因此,經度的部份是從最左邊的-180到最右邊的180度,平均均分地圖上的所有x軸距離。
於是我們可以很簡單的計算出某一個經度應該對應到實際像素裡的哪一個x值,因為經度是平均切割整個世界地圖的,所以純粹只是比例的換算而已。
麻煩的在於緯度。
緯度並不是平均切割地球的從南到北,越靠近極點,形變就越大。
感謝wiki,我們不用自己去導出投影的公式,wiki已經幫我們列出來了:

上半部是由經緯度計算出x及y的值,下半部是由x跟y的值反算出經緯度,由於我們要將經緯度用在GoogleMap圖片的像素值上,因此我們只要再加上一些參數做修正其實就可以了。

結果
我花了一點時間實作了一下,結果在這邊:

http://labs.medialand.com.tw/jason/flash/googlemap/
原始檔下載
輸入經緯度及放大級數,這flash就會把周邊的地圖載入進來。
其它的,例如:拖動地圖…等等的,我就沒時間再寫了,反正都已經能正確取得圖片及計算出座標值,其它的就是另外一種工作了。
比較需要注意的是
1. 這只是簡單的地圖,畢竟不能跟完整個GoogleMap相比(當然你要寫得超屌也可以)
2. 經過測試,無法去draw載進來的地圖,crossdomain的關係。
3. 一樣是crossdomain的關係,其它的GoogleMap api,像是地址反查這種功能,在web的flash裡也無法直接去做query,可能還是得透過js去做中繼。
4. 這是旁門左道,非必要的話還是請愛用GoogleMap v3。


2012年2月16日 星期四

JS+CSS練習 - 米蘭數位官網HTML版本



持續的玩CSS及JS中。



因此拿了米蘭的官網來練習。
米蘭官網連結在這ㄦ - http://medialand.com.tw/home.htm
練習結果在這ㄦ - http://labs.medialand.com.tw/jason/html/ml/

比官網少了一些單元,因為跟資料庫要資料的部份我(目前)懶得去寫…。
一開始我傻傻的,想說在畫面上轉來轉去的那些球,「搞不好」可以直接用dom來跑就好,畢竟只是scale的縮放,沒有複雜的變形,只要把透視給算好就ok了。
於是就試寫了第一版,下場就是效能十分之慘。
苗頭不對之下,只好乖乖的用canvas來處理這些轉圈圈,效能果然好很多了。但由於老IE不支援canvas,所以我仍然保留原本的dom版本,只要判斷瀏覽器不支援canvas,就會把dom拿出來跑…。

練習的作品我是盡量把它寫得很像原本flash版本的效果,我也是用像寫AS的概念一樣去安排配置畫面上的東西,但只能盡量,多少還是有一點點的差異,不過畢竟是練習作,練習中學到的經驗比較重要。
主要的一些(奇怪的)心得就是:
1. 把div當成DisplayContainer來用XD…。
2. 空白的div可以拿來當按鈕感應區…。
3. 還沒找到很好用的tween(好啦,我自己也沒很用心的去找),現在只能自己用setInterval去跑,或是簡單的就交由JQuery的animate去跑。
4. Canvas要判斷滑鼠是否over到某顆球上的這件事,小麻煩啊…,尤其是兩顆球如果疊在一起就更討厭了,現在只是球狀還好,之後如果寫到複雜圖案的時候,就會又點傷腦。
5. 除了canvas以外,其它的東西其實一熟練的話處理起來還算快,反正遇到困難的結構就是二話不說多加幾個div來當DisplayContainer!!(但我先說,這可能不是很正統的做法XD)

2012年1月16日 星期一

Javascript用prototype進行繼承會發生的奇怪現象



這問題真的很奇怪,而且很難理解。
測試網址如下(code很簡短,可直接看原始碼):
http://labs.medialand.com.tw/jason/html/inheritance/test1.html

狀況是,
1. 建立一個Test的類別,在建構子時宣告並初始化一個陣列testValue。
2. 在Test類別建立一個function addValue,用來將傳入的值push到testValue裡。
3. 在Test類別建立一個function destroy,用來將testValue清空。
4. 建立一個AA類別繼承Test類別。
5. 實體化一個AA類別,變數名稱a1,呼叫a1.addValue(123)將123這個值push到a1.testValue裡。
6. 呼叫a1.destroy()來清空a1.testValue。
7. 目前看起來都正常,怪的來了。
8. 實體化另外一個AA類別,變數名稱a2。
9. 直接取得a2.testValue,居然不是空值,而是123!!!
照道理說,testValue裡有123這個值,不管怎樣也是屬於a1這個instance的,不應該會出現在新new出來的instance裡。更何況在a1的時候已經執行過一次destroy來清掉testValue了。
無法理解。
這種情況目前測試,只發生在testValue非一般常數型態的變數上,數字跟字串不會發生這種情況,在array或object上就會發生這種異常的狀況。

解決方法應該有很多,目前我覺得我自己最喜歡的方式,就是在AA類別的建構子裡再一次強制呼叫父類別Test的建構子,就像AS3裡的super();一樣。
再測過一次後這個問題就會被解決。
修正過後的測試網址如下(差別只在於AA的建構子裡多了一行):
http://labs.medialand.com.tw/jason/html/inheritance/test2.html