thefrontrowblog
thefrontrowblog
The Front Row
9 posts
The Front Row is a blog by Zhusee (@zhusee2), writing about notes or techniques about front-end web development.
Don't wanna be here? Send us removal request.
thefrontrowblog · 12 years ago
Text
Infinite Scroll 自動捲動換頁的客製化選項
Infinite Scroll 是 Paul Irish 編寫維護的一套自動換頁 plugin。他的運作原理是透過 JS 去讀取網頁上的分頁連結 (Pagination)、然後在網頁捲動到接近分頁連結位置的時候自動去抓下一頁的內容、然後插入到目前內容的結尾處。像是 Pinterest 這類的 UI 用的就是類似的概念。
基本用法
Infite Scroll 的基本用法是,你先指定好用來包內容的元件 (Content Container),然後告訴他分頁連結在哪裡、網頁裡面要抓什麼元件回來,剩下的他就會幫你搞定。甚至還幫你準備好了 loading 的提示動畫,會幫你插在目前內容的尾���。假設我是一個公告系統,想要讓我的公��可以一直無限捲動的話,可以這樣寫:
只要這四個基本要素,就可以完成無限捲動的自動換頁功能了。另外文件上也有提到,如果你中間想要暫停或恢復無限捲動的功能,可以使用 pause 跟 resume 方法:
$('#announcements').infinitescroll('pause'); $('#announcements').infinitescroll('resume');
客製化 Infinite Scroll Plugin
Infinite Scroll plugin 蠻簡單好用,不過如果需要修改部分預設行為、讓他可以跟其他程式互動的話,文件上就沒有寫得那麼詳細了。下面介紹兩個可自定的選項,讓 Infinite Scroll 更彈性。
在取回次頁內容後做加工處理
一開始在設定 Infinite Scroll 的時候,可以指定一個 callback 函數給它;它會在次頁的內容已經被抓回來、並且插入 container 之後被呼叫。用法是在那個選項的 hash 後面多傳一個函數作為參數:
你在 callback 裡面會拿到三個參數,分別是:
剛抓回來的元件陣列 (Array)
Infinite Scroll 目前的選項清單 (Hash)
剛抓回來那頁的位址 (String)
由於這個 callback 是在 Infinite Scroll 內部的 _loadcallback() 當中被呼叫,呼叫的時候抓回來的元件(例如上面的 .post)已經被插在容器元件裡面了。不過還算來得及多做點什麼加工。
自定載入中提示
Infinite Scroll 在載入次頁內容的時候,會幫你顯示一個「載入中」的動畫與提示文字。如果你希望可以用別的方法來提示使用者目前正在載入中,而不想要用它預設的的方法的話,你可以傳兩個函數去分別處理「開始載入」跟「載入完成」時的行為。傳入的方法是在一開始設定的時候,先傳進它的選項裡面:
只要有指定 start 時的 function,預設的提示動畫就不會出現。但是特別要注意的是,在 Infinite Scroll 中啟動 Ajax 的部分是放在預設的 loading.start 裡面的。所以如果你用自定的 start function 去蓋掉了預設的函數,就要手動去告訴 Infinite Scroll 開始跑 Ajax 抓內容。
首先你要先拿到 Infinite Scroll 目前綁定在容器元件上的物件實體 (Instance),然後對它呼叫 beginAjax() 方法;同時要把你在 start function 裡面拿到的參數(那是 Infinite Scroll 目前實體的設定清單)傳給 beginAjax(),它才會開始動。還好 Infinite Scroll 會把它的物件實體掛在容器的 jQuery Data 上,只要透過 $(container).data('infinitescroll') 就可以拿到它的物件實體了。
在處理 finished 的時候,通常就是把你自定的載入提示給關掉,這邊應該比 start 簡單。
偵測是否已經捲到底(已經抓到最後一頁)
對 Infinite Scroll 來說,抓到最後一頁的時候會因為「沒有再下一頁可以抓了」而觸發一個「done 錯誤」。所以如果你需要在捲到底的時候做些什麼事情(例如顯示自定的提示訊息),你需要把你的程式碼掛在 errorCallback 下面。
掛在 errorCallback 的函數,會在發生各種錯誤的時候被觸發。你會拿到一個參數,告訴你現在是發生什麼錯誤。如果它是 "done",就表示你捲到底了。
參考資源
Infinite Scroll Github Repository
3 notes · View notes
thefrontrowblog · 12 years ago
Text
Web Speech API (I): 使用 SpeechSynthesis 讓瀏覽器講話
Google 在去年以 Community Group 的方式,向 W3C 提交了 Web Speech API 的規格書。該 API 主要分為兩部分:
語音轉文字 (Speech Recoginition)
文字轉語音 (Speech Synthesis)
其中 Chrome 實作語音轉文字的語音辨識功能很久了,不過似乎並沒有急著實作文字轉語音的部份;反倒是回歸 Apple 掌握的 WebKit 最近實作了無 vender prefix 的 Speech Synthesis 相關 API,目前 WebKit Nightly 就玩得到,所以來玩玩看。下一版的 Safari (6.1/7.0) 也預計會支援此 API。
本篇之前於 HappyDesigner Mini 分享會 #2 分享,實際效果可參考 Demo 影片 或是 Demo Page:
基本概念
最基本的來說,要讓你的瀏覽器講話,只要達成下面兩個步驟:
建立一個 SpeechSynthesisUtterance 類別的物件,代表「要講出聲的話」。
把 utterance 物件交給 window.speechSynthesis 使用 speak 方法來念出來。
你可以一次把多個 utterance 物件經由 speak 方法指派給 window.speechSynthesis,他會被加入一個發聲的佇列、依序被講出來。
調整 Utterance 設定
下面是 SpeechSynthesisUtterance 類別的規格。要調整 utterance 的設定可以從這邊看:
除了相關事件的部份省略以外,上面可以修改的屬性都蠻望文生義的。特別要提到的是 voice 這個屬性並不在 Web Speech API 的規格裡面,是 WebKit 額外實作的屬性,用來更換發音用的語音引擎。
text 屬性是這段 utterance 裡面要被念出來的文字。裡面可以是純文字或是 SSML,而且最多不能超過 32,767 個字元。
更換語音引擎 (for WebKit)
就 Web Speech API 規格來說,本來的設計是透過 utterance 的 voiceURI 屬性來指定不同的語音引擎。但是因為 WebKit 在 OS X 上是直接透過 OS X 內建的 VoiceOver 系統來實作,所以不使用指定 URI 的方式來指定引擎。雖然一般來說透過指定 utterance 的語言就可以達到更換語音引擎的效果,但是以 OS X 下面來說,部分語言(如英文)就有多個不同的語音引擎可以使用;或是中文文字可以使用國語(zh-TW)/普通話(zh-CN)/廣東話(zh-HK)來發音,所以需要時還是可以依需要做更換。
在 WebKit 下更換語音引擎的方法是:
透過 window.speechSynthesis 的 getVoices() 方法取得目前系統中所有可用的語音引擎清單。
清單中每個項目是一個 SpeechSynthesisVoice 類別的物件,裡面的屬性可以取得語��引擎支援的語言(lang)、引擎名稱(name)跟其他相關資訊。
從清單中過濾出要使用的語音引擎,然後把它指派給 utterance 的 voice 屬性(WebKit only)。
可參考下面的範例:
在範例中,清單中第 45 個(44 號)語音引擎剛好是講廣東話的 SinJi Compact;所以指派給 u 之後,講出來的話就會使用廣東話發音。
偵測語音狀態
這邊可以再複習一次,使用 SpeechSynthesis 讓瀏覽器「講話」的方法是:建立一個一個 utterance 物件,來描述要講話的內容、然後交給 window.speechSynthesis 的 speak() 方法來負責播放語音。如果要偵測語音的狀態,來跟其他部分做配合,可以就兩個層面來偵測。
偵測「瀏覽器是否正在講話」
window.speechSynthesis 物件下面有三個屬性,可以了解目前有沒有正在講話:
pending: 表示 synthesis 物件的佇列裡面有還在排隊等著發聲的 utterance 物件
speaking: 表示有 utterance 物件已經開始唸、但是還沒講完。即便是唸到一半暫停的狀態,speaking 也會是 true
paused: 表示有 utterance 物件被唸到一半,但是暫停中。
偵測 utterance 物件的狀態
你無法直接偵測 utterance 物件的狀態,但是可以透過註冊事件的方式來在不同情況時獲得通知。utterance 物件可用的事件有:
start: 這段 utterance 開始發聲
end: 這段 utterance 的文字全部講完了
error: 發生錯誤
pause: 講到一半暫停
resume: 這段 utterance 在暫停之後恢復繼續。文件中提到被加入 queue 的 utterance 是處於「暫停」狀態,但是輪到他發聲的時候因為是第一次開始,所以觸發的會是 start 事件而非 resume 事件。
mark: 如果你是指定一份 SSML 給 utterance 當做要念的內容,會在唸到「mark」標籤的時候觸發。這部份請參考 SSML 文件。
boundary: 當發音引擎念完(觸及邊界)一個單字 (word) 或一個句子的時候觸發。
其中比較常用的應該是 start、end、pause 跟 resume 事件。
向 utterance 註冊獲得的事件是 SpeechSynthesisEvent 類別,裡面可以獲得這段 utterance 從開始發聲開始經過的時間 (elapsedTime) 跟目前所講到的位置 (charIndex)。
目前的 WebKit Nightly (v538.1+) 似乎在 charIndex 部分回傳都是 0;另外目前也還無法使用 addEventListener 方法直接向 utterance 物件註冊事件,只能使用如 onend 的屬性直接指定事件處理的 function。
Demo
在 HappyDesigner Mini #2 的時候,本來想要用 Web Speech API 做一個網頁版的 Siri。不過因為語音辨識(Speech Recognition)只有 Chrome 有實作、文字轉語音(Speech Synthesis)只有 WebKit/Safari 有實作、再加上時間不夠(?)作罷。但是成功做出了一個可以針對(一個)關鍵字做反應、然後把結果念出來的 Demo。
效果可參考本篇最上方的 Demo 影片、或是有興趣的話可以下載 WebKit Nightly 之後實際玩玩看。
參考資料
Web Speech API Specification
WebKit Nightly Builds
1 note · View note
thefrontrowblog · 12 years ago
Text
在 Rails 下模組化整理 CoffeeScript
在 Rails 下面寫 JavaScript 跟 CSS 很方便,因為你可以把 .js 跟 .css 以模組化的方式拆成很多小檔案、然後以階層目錄之類的方式來整理檔案。反正最��上 Production 後、AssetPipeline 會幫你把眾多小檔案,以你指定的順序結合成一包大檔案一起載入──例如 application.js 或 application.css。
對 CSS 來說這樣整理的方式沒有問題,因為 CSS rules 是只要宣告過就會留在那邊;只有先後順序跟優先層級問題,沒有跨檔案問題。對純 JavaScript 來說其實也蠻好解決,例如 CSS-Tricks 的《How Do You Structure JavaScript? The Module Pattern Edition》 就建議你可以把 script 以模組化的方式包成一個個物件、就可以一個模組一個檔案拆下去整理;最後再依序包回來使用即可。CSS-Tricks 的範例大概如下:
這樣的方法很方便、而且可以把 JS 檔案整理得很漂亮。但是如果你寫的是 CoffeeScript 就麻煩了。
CoffeeScript 拆成不同檔案的麻煩
雖然在 CoffeeScript 下面因為寫 class (相對)非常容易,所以把 code 模組化成各種 class 是最方便的方法;但是問題就出在 CoffeeScript 每個檔案 compile 完出來的 code,都是放在一個立即執行的匿名函數 (anonymous function) 裡面。這個優點在於你的所有變數,會被限制在匿名函數的 scope 裡面而不會向外污染、壞處就在於當你想採取上面 CSS-Trick 的那個模組化整理方法時,你會發現:每個 class 都因為 scope 的關係被限制在個別檔案裡面,根本無法互相溝通。
平常專案如果 script 沒有用 class 用很兇、都只是用 jQuery 在處理頁面元件的話,scope 基本上也不是什麼問題──反正 jQuery 本身就被註冊為全域變數了,一定拿得到。第一次深受其害是在做 Logdown 的時候。Logdown 的編輯器非常倚賴 JavaScript,所以我一開始的時候把它不同部位做成不同 class、最外面再用一個總 class 去管理下面的不同部位。隨著 script 越寫越長、整份 editor.coffee 也變得越來越難讀(因為實在太大了)。等到開始想把他依照模組化的方式,拆成不同小檔案出去的時候,就遇到上一段的窘境了:姑且稱為「scope 陷阱」好了。
解決「Scope 陷阱」的初步嘗試
事已至此,眼前擺著兩個相當直覺的解法可以考慮:
為了 scope 大局著想,裝死繼續把所有會用到的 class 全部包在同一個 .coffee 裡面。
那就打破 scope,要求 CoffeeScript 在 compile 的時候不要包匿名函數吧!
直覺是都很直覺,可惜其實蠻掩耳盜鈴的。1 的話根本就什麼也沒做、2 的話則是會導致全域變數一瞬間被倒入很多 class。本來不想污染 global 的、這下可全部都倒進去了。看來只好使用密傳的方法 3 了:
Tumblr media
(圖片來源:國立故宮博物院)
最後解法:在全域掛一個 Application Namespace
最後我想到:既然平常引入比較大的外部 Library 其實也都會至少多掛一個全域變數(像 jQuery 就一次註冊了 jQuery 跟 $ 兩個),那我整個專案也註冊一個全域變數應該也不會很過分吧。我只要在一開始先在全域下面掛一個 namespace、之後每個 class 都掛在 namespace 下面就好了。範例大概如下:
這樣就可以突破 scope、在 CoffeeSript 之間跨檔案取用 class;可以依然用模組化的方式去拆開跟整理 .coffee 檔案,又不會過度污染 global 了。
0 notes
thefrontrowblog · 12 years ago
Text
HTML5 File API 筆記 (1) 基本名詞解釋
HTML5 多了 File API,可以透過 JavaScript 來處理到本機的檔案。下面有幾個 Interface,稍微筆記一下:
FileList
HTML5 的檔案欄位支援選取多個檔案一次上傳,只需要加上 "multiple" 屬性。要拿到檔案欄位目前選取的檔案清單,可以直接跟該欄位要 files 屬性:
<input id="fileField" name="files[]" type="file" multiple /> var fileList = document.querySelector('#fileField').files
這樣會回傳一個 FIleList 物件回來,結構上類似陣列,有 length 屬性、也可以用 item(n) 的方法要到清單中的第 n 個檔案。其實也可以直接像陣列一樣直接跟他要 fileList[n]。這份檔案清單裡面放的是 File 物件的集合,下面會提到這個類別,但總之就是一個個檔案。
不過講這麼多,不如直接看 console 下面拿���什麼:
Tumblr media
Blob
Blob 在 Spec 上面寫是「 不可變的 原始資料」(immutable raw data),我在這邊解讀為檔案的原始碼(不是給肉眼判讀的形式)、或稱資料片段。Blob 物件有兩個屬性:
size: 傳回 Blob 物件的大小(單位是 bytes)
type: 傳回 Blob 物件的 MIME type(例如 image/png)
Blob 物件另外帶有 slice 方法跟 close 方法,前者可以從 Blob 當中切出更小的 Blob(也就是擷取其中的片段)、後者則顧名思義是把這個 Blob「關掉」—從結果上來看就是這個 Blob 就不能再使用了。
File
既然 Blob 本身就是代表一個/一段資料或檔案,那 File 繼承 Blob 也是合情合理。從上面 FileList 的範例圖中,也可以看到 File 物件帶有跟 Blog 物件相同的屬性。
不過 File 類別比起 Blob 多了兩個屬性,分別是 name 跟 lastModifiedDate:相信這兩個屬性應該「一望即知」,不需要多做解釋。
實際要把檔案讀進來使用,需要用到 FileReader Interface,這個分第二篇來筆記。
1 note · View note
thefrontrowblog · 12 years ago
Text
關於在 iOS WebView 上取得即時捲動位置的實驗
TL;DR: 在慣性捲動的時候 JS 似乎會被暫停執行,所以沒辦法在中間做點什麼
今天在 Twitter 上看到 @medicalwei 的一篇推文,在問怎麼在 iOS 上處理捲動事件。點進去看,原來是他有一個專案是透過偵測捲動的位置、即時更新某一條 indicator。因為好像很有趣,我就也跳下去實驗看看。
tinyurl.com/chkwqwk iOS 要怎麼處理捲動事件……即使改用 touchmove,慣性這部分我摸不到 Orz plurk.com/p/il9bld
— Yao Wei (@medicalwei)
May 12, 2013
首先是找一點跟 Touchevent 相關的資料。
iOS WebView 捲動網頁的事件流程
根據 Safari Web Content Guide 中 One-Finger Events 段落所描述,在 WebView 中用一隻手指做捲動的時候,整個流程應該如下圖:
Tumblr media
手指按到螢幕,準備開始捲動 (touchstart)
手指在螢幕上移動中,會觸發一到多個 touchmove 事件
手指在螢幕上停止,觸碰結束 (touchend)
因為慣性捲動 (Inertial Scrolling),網頁繼續減速度捲動。但此時不觸發任何事件
網頁停止捲動,此時終於觸發 scroll 事件
所以在整個捲動的過程中,只有從 touchend 到 scroll 中間是事件的真空狀態 — 也就是從手指在螢幕上的捲動手勢結束之後、到網頁真的停止捲動之前。其他時候都可以透過聽 event 的方式來取得目前網頁的捲動位置。
實驗方法
不過雖然這中間不會觸發事件,應該也不代表就沒辦法做?所以直覺上來說就想到兩個方法:
計算手勢停止瞬間的捲動速度、然後用 JavaScript 模擬慣性捲動的速度遞減,推估每一小段時間應該要捲動到的位置、然後以小單位時間定期「回報」這個估計出來的位置
直接跑 window.setTimeout,在慣性捲動的這段空白中定期觸發一個 function,來即時取得目前網頁的捲動位置。
實驗結果:失敗
但很不幸地,實驗結果是失敗的。經過多次實驗,我發現當 iOS WebView 正在做慣性捲動的時候,setTimeout 就會被暫停觸發。所以上面兩個基於 setTimeout 的方法基本上就 — 做不到。
以下面的 script 來說
實際上也還是只拿得到兩次:一次是在 touchend 觸發的時候、一次是在慣性捲動結束後、scroll 事件還沒有把 flag 關掉之前。事實上,如果在網頁一載入就開 logScroll() 且不設定停止條件,在捲動手勢後的慣性捲動中(也就是在 touchend 到 scroll 之間)的 console log 也會斷掉。
結論
目前實在的結論是沒有找到可行的方法。我推測這個設計也許是因為在慣性捲動的時候,WebView 實際上是把整個網頁畫面當成材質丟給 GPU 去運算捲動的動畫。所以在這中間即便 JS 改動網頁什麼、也要等慣性捲動停止後才會做 repaint;或是為了某種資源上的考量,就把慣性捲動期間的 JS block 住了。
不知道還有沒有其他可能的解法?
1 note · View note
thefrontrowblog · 12 years ago
Text
使用 jQuery Deferred Object
jQuery 從 1.5 開始引進了 Deferred Object(延遲物件),可以更簡便地處理非同步程式在不同狀態的 callback。以往最常用到 callback 的就是 AJAX:當抓取資料結束的時候、失敗的時候、或是成功的時候,都會呼叫指定的 callback 函式來執行相對應的動作。現在 Deferred 物件可以讓其他需要卡很久的程式碼也丟出去非同步執行、再使用 jQuery 方便的 API 來處理 callback。
Deferred 物件的概念
一個 Deferred 物件有三種狀態:一開始是 pending(未解決)、另外有 resolved(已解決)跟 rejected(已拒絕)。從字面上應該很好理解,pending 就是還在等、resolved 就是順利成功了、rejected 則是失敗了。
想像一個叫「大F」的大牌藝人,每次通告都跑很久。如果有個節目放大F去出外景任務,可是要所有人等他不知道什麼時候回來,才能繼續錄影的話,那實在太痛苦了。所以幫大F準備了一個 Deferred 經紀人(叫「D姊」)。導演先跟D姊商量好,大F任務成功的時候、失敗的時候、新破關的時候要做些什麼事情,然後攝影棚內就可以繼續開拍了。中間可以跟D姊詢問大F任務完成了沒,其他跟大F相關的工作,就等到他外景出完了再說。
「大F」就是可能需要等很久的程式,所以我們可以把它包成函數丟出去做非同步執行、然後請 Deferred 物件(也就是「D姊」)幫忙管理 callback 的執行。
建立一個運用 Deferred 物件的工作
通常使用下列兩種方法:
在函數內自行建立 就是在你要做非同步處理的函式裡面,自己建立一個 Deferred 物件、然後回傳出去給外面的程式使用。
function longWaitingTasks() { var deferred = $.Deferred(); // Do a lot of tasks here return deferred.promise(); }
透過 jQuery.Deferred 函數建立 jQuery.Deferred() 函數本身可以傳另一個函數進去,這樣在執行的時候,會把新建的 Deferred 物件當成第一個參數,傳進去剛剛丟給 jQuery.Deferred 的那個函數。後者的 this 也會指向剛建立的 Deferred 物件。
function longWaitingTasks(deferred) { // Do a lot of tasks here return deferred.promise(); } $.Deferred(longWaitingTasks);
前面提到,非同步函式的 callback 是由 Deferred 物件幫忙管理的,所以我們要設定 callback 函式的話都要使用 Deferred 提供的方法。
這邊兩個方法都在結尾回傳了 deferred.promise(),這是為了讓外面的程式碼不會直接拿到可以改變狀態的 Deferred 物件,而是拿到一個只能向 Deferred 物件查詢狀態或設定 callback 函數的 Promise 物件。
對 Deferred 物件設定 callback
一開始的時候提到,Deferred 物件可以方便的管理 callback。到底有多方便,大概��像這樣:
deferred.done(callback) #=> 成功時執行 deferred.fail(callback) #=> 失敗時執行 deferred.progress(callback) #=> 還在跑,但是裡面的程式使用 `.notify` 方法通知進度 deferred.always(callback) #=> 無論成功或失敗都會執行 deferred.when(filters) #=> 在呼叫 callback 前先處理資料,後面解釋
這些方法都只有 Deferred 或 Promise 物件有提供,所以設定 callback 的時候不是直接向負責工作的函數設,而是向函數提供給你的 Promise 物件設定。
觸發 Deferred 物件的 callback 函數
Deferred 物件會兩種時機被觸發 callback 函數:當 Deferred 物件狀態改變、或是被「通知」(Notify)。前者應該是最常見的了。
改變 Deferred 物件狀態
Deferred 物件在一建立的時候是 pending,而後當你的非同步函數確定結果了之後,就會需要改變對應其的 Deferred 物件狀態;此同時也會觸發 callback。
deferred.resolve([args]) #=> 狀態改變為「成功」,觸發 .done callback deferred.reject([args]) #=> 狀態改變為「失敗」,觸發 .fail callback deferred.resolveWith(context, [args]) deferred.rejectWith(context, [args])
這四個方法都可以選擇性傳入參數,他會幫你把參數轉給 callback 函數。不同的是,後兩者傳入的 context 會變成 callback 函數下面的 this 物件。狀態轉為成功會觸發 deferred.done(callback) 傳入的函數、轉為失敗則會觸發 deferred.fail(callback) 傳入的函數。而無論成功或失敗,都會觸發 deferred.always(callback) 所傳入的函數。
「通知」Deferred 物件
另外一種情況是,你事情可能做到一半,但是要即時回報目前進度,免得使用者以為你當掉了。這時候可以使用 deferred.notify() 方法,他會觸發 deferred.progress(callback) 傳入的函數。跟上面一樣,你也可以傳一個 context 物件進去:
deferred.notify([args]) deferred.notifyWith(context, [args])
結合多組 Deferred 物件一起 callback
有時候也許會希望幾件不同的事情都做完之後、才一起觸發完成的 callback。這時候可以使用 $.when() 方法。$.when() 可以傳入任意數量的 Deferred 物件,之後他會回傳一個統合的 Promise 物件。這個統合的 Promise 是由一個新的「主要」Deferred 物件產生,而後者會負責追蹤傳進 $(when) 裡面的所有 Deferred 物件。他會在所有 Deferred 物件都成為 Resolved(都成功了) 之後,觸發 .done(callback)。但只要裡面有任何一個 Deferred 轉為 Rejected(失敗了),就會立即觸發 .fail(callback)。
目前實測的結果是,當所有 Deferred 都完成後,註冊在 $.when() 下面的 callback 會拿到第一個 Deferred 物件傳給 callback 的參數:
var d1 = $.Deferred(), d2 = $.Deferred(), w = $.when(d1, d2); w.done(function(msg) { console.log(msg) }); d1.resolve("Part A done"); d2.resolve("Part B done"); #=> "Part A done"
如果傳進 $.when() 方法裡面的不是 Deferred 物件,那被傳入的參數就會被視為一個已經 Resolved 的 Deferred 物件,所要傳給他的 .done(callback) 的參數。
Deferred 物件的 Filter (v1.8 適用)
一般使用情況下,在工作函數裡面會直接存取 Deferred 物件來改狀態或發通知、而另外回傳 deferred.promise() 生出來的 Promise 物件給工作函數外的程式來查詢 Deferred 物件狀態或設定 callback。但是如果想要在 callback 函數被觸發前先處理過 Deferred 物件傳出來的資料,可以使用 deferred.then() 方法。then() 方法會回傳一個新的 Promise 物件,可以再這個新的 Promise 物件設定 callback,這樣就可以達到中間過濾/處理資料的��的。舉例來說:
function longWaitingTasks() { var deferred = $.Deferred(); setTimeout(function() { deferred.resolve("Mail Sent"); }, 5000); return deferred.promise(); } var dTask = longWaitingTasks(); var filteredTask = dTask.then(function(msg) { return msg + "-modified"; }); filteredTask.done(function(msg) { console.log(msg); #=> "Mail Sent-modified" });
所以基本上是走了這樣的路線:
本來的 Promise → 過濾後的新 Promise → callback
不過暫時還不太理解會在什麼時機用到這個 Filter⋯⋯
注意: 本段的 .then() 方法是 jQuery 1.8 以後的最新用法。如果你用的是較舊版本的 jQuery,請回頭參考官方 API 文件
[更新] 簡易使用範例
參考資料/延伸連結
jQuery的deferred对象详解(阮一峰)
jQuery Deferred Object API
2 notes · View notes
thefrontrowblog · 12 years ago
Photo
Tumblr media Tumblr media Tumblr media
## 使用 CSS box-shadow 模擬彎曲陰影 (以 Apple 首頁為例) 最近 Apple 首頁也加入了彎曲陰影的效果,好奇之下就點開來看一下他怎麼做的。他不是使用之前已經有人做過的 [pseudo element + box-shadow](http://nicolasgallagher.com/css-drop-shadows-without-images/) 來做,而是直接透過多層 wrapper 的元件來實作陰影效果。 #### Concept 陰影效果的概念是: 1. 用一個以 `border-radius` 做出圓邊的元件、 2. 幫他開 `box-shadow` 之後 3. 放到想要套用彎曲陰影的元件後面(被遮住)。 最上面的圖共有三張,切換之後可以看整個陰影技巧的示意圖。黃色這個就是藏在背後,負責顯示陰影的元件(無論你是用真的 element 還是 pseudo element)。 #### border-radius `border-radius` 的作法也要留意一下,如果要做出上圖第三張(黃色邊框)這種形狀的話,需要給定他第二個半徑讓他把邊框的圓角做成橢圓形。Apple 這邊給的設定是: border-radius: 100% / 33px; 其中 100% 是 x 軸的第一個半徑、33px 是 y 軸的第二個半徑。如果只給定一個半徑,整個邊框會慢慢往橢圓形或圓形邁進,做出來的陰影曲度可能會超過你所想要的效果。 #### 把陰影放到目標元件的「背後」 這邊講的**前後關係**對應到網頁上來說就是相對的 z 軸高度。如果你有玩過 Firefox 網頁檢閱器裡面有個「3D 檢視」,會發現每個子元件像是一層一層貼紙往上堆似的,堆在 parent element 的上面。越上面的就是越**前面**。 Apple 這個多層元件的做法是以最前面產品圖片的上一層 parent element 來當作負責顯示陰影的元件。因為通常 parent element 會顯示在 child element 的後面被蓋住,就不需要再特別去調 `z-index` 的前後關係。 如果用 pseudo element 做的話,不管是 `::before` 或 `::after` 他都被視為是該物件的子元件,所以這時候會需要用 `z-index: -1` 之類的方法把它硬是送到元件的後面去,才不會反過來被整片陰影蓋住。
3 notes · View notes
thefrontrowblog · 12 years ago
Text
使用 CSS Radial Gradient 模擬彎曲陰影
其實我也不知道用中文到底怎麼稱呼這個東西,找了一下英文叫做 Curved Shadow。 ![彎曲陰影示意圖](http://media.tumblr.com/a9d7abb13dbe05cd9e129e28d8231809/tumblr_inline_mfmcx9JQEO1qzdn7v.png) 這類的陰影其實也蠻常見了,上圖示範的是有點像紙張中間隆起造成的陰影、另外一種常見的是紙張中間凹陷、兩邊捲起造成的陰影。最直覺上就是在 Photoshop 裡面先做好再轉成圖片出來,不過我一直都很懶得做圖片—另一方面,圖片也蠻不彈性的。如果你要套陰影的主體寬度改變了,圖片感覺很難對應的很好。所以我觀察了一下,決定使用放射狀漸層來模擬這個效果。 ![放射狀漸層模擬彎曲陰影概念圖](http://media.tumblr.com/8e457efbc9414e9a76e3838abc9769fc/tumblr_inline_mfmktnmcK71qzdn7v.jpg) 實作起來的概念大概就像上圖那樣,畫個由裡到外淡出的橢圓形漸層、把上半部蓋掉、然後再加一條邊線來當做陰影與物件主體的邊緣線。橢圓不要太高,看起來就有一點彎曲陰影的感覺了。而且需要的 code 也很簡單,只需要一行: background-image: (-vendor-)radial-gradient(top center, 50% 5px, rgba(0,0,0,.2), transparent); 其中 `50%` 是 x 軸半徑、 `5px` 是 y 軸半徑,top center 為漸層中心點位置。不過有關 [CSS Gradient][1] 的語法,後面被 CSS Working Group 又翻修過了一次,所以如果要確保未來瀏覽器慢慢更新成最新標準後也能正常顯示,可以多加一行最新標準: background-image: radial-gradient(50% 5px at top center, rgba(0,0,0,.2), transparent); 成果就像本文最上面那張圖了。 同樣的效果,之前也有不少人做過,例如 Nicolas Gallagher 這篇是[用 pseudo element + box-shadow 來實作][2]:中央凸起的 curved shadow 就搭配 `border-radius`、兩邊捲起的 page curl effect 則用兩組 `box-shadow` 搭配 `transform: rotate()` 來實現。效果也都不錯。 [1]: http://dev.w3.org/csswg/css3-images/#gradients "CSS Gradients" [2]: http://nicolasgallagher.com/css-drop-shadows-without-images/ "CSS drop-shadows without images by Nicolas Gallagher"
3 notes · View notes
thefrontrowblog · 12 years ago
Text
CSS3 Columns: 消失的項目符號
![CoverTunes 實際成品效果示意圖](http://media.tumblr.com/3cd3a647a99dccf6a5b621fe6703a4bf/tumblr_inline_mfjreymprx1qzdn7v.png) 之前在做 [CoverTunes][1] (如上圖,註1) 的時候,為了實作 iTunes 11 新的 Expanded View 會隨著視窗寬度把專輯裡面的曲目清單分配為多個欄位的這個特點 ,所以我用了編號清單 `
` 搭配 CSS coluun 下去做: (-vendor-)column-count: 2; (-vendor-)column-gap: 40px; 結果欄位分得是蠻平均的,左右兩邊的 `
` 數量差不多 —可是在前面的編號就*不見了*— 在 Webkit 下是直接消失;在 Firefox 下面則是只有右邊那欄位出現。 ![項目符號消失示意圖: Firefox 17 vs. Safari 6](http://media.tumblr.com/467af9de15b0e0c15c9ed3c500182bc3/tumblr_inline_mfjfe9CNGg1qzdn7v.jpg) 結果問題其實很簡單:因為 `
` 的項目符號預設是畫在他本身 box 的外面,而下了 CSS columns 之後,大概是為了把內容對齊 coloumn ,所以超出去的部份就被切掉了。然後剛好 WebKit 切的跟 Firefox (Gecko) 不一樣。 所以解法有兩種,一種是對 `
` 下個 `margin-left` 推出一點空間給他顯示項目編號就好了;或是可以指定他 `list-style-position: inside;` 讓他把項目編號畫在 `
` 本身的 box 裡面,就不會因為在 column 下沒有幫 `
` 保留前面 bullet 的空間而消失了。 ![項目符號正常顯示示意圖: 套用 list-style-position: inside; 之後](http://media.tumblr.com/bec72107668b22be4babb7a2f64dd01b/tumblr_inline_mfjfl8jxlB1qzdn7v.png) --- ### 註解 1. [ColorTunes][2] 為我與 [@dannvix][3] 合作的專案。我做的 [CoverTunes][1] 構成 iTunes Expanded View 外觀與互動效果重製;[@dannvix][3] 則實作根據專輯圖片自動變色的功能。兩者合一為 ColorTunes [展示頁面][2]。 [1]: https://github.com/zhusee2/coverTunes/ "CoverTunes Repository on Github" [2]: http://dannvix.github.com/ColorTunes/ "ColorTunes Demo Page by @zhusee2 and @dannvix" [3]: http://twitter.com/dannvix "@dannvix on Twitter"
0 notes