如何用捷徑給 GIF 加上進度條
看到一張 GIF,可把我笑壞了,最後那隻汪也太不給面子了吧哈哈哈哈~

這樣的圖片,就是利用 GIF 迴圈播放特性做出來的趣味效果。
但是給文章配圖時, 有些 GIF 太長或沒有明顯的起始、結束標誌,此時讀者可能會產生一種焦慮感:這個 GIF 什麼時候完?我到底是不是已經看完一遍了? 其實,加個 進度條 就能解決問題,比如這樣:

實現進度條的原理也並不複雜:假如某個 GIF 共有 n 幀,那麼播放到第 i 幀時,其進度(以小數計)就應該是 i÷n。我們考慮往這一幀上新增一個進度條,設幀寬度為 width,進度條的長度就應該是 width×i÷n,是不是?具體的實現方法有許多,可以使用 Photoshop 等專業軟體來做,本文介紹一種簡單的方法:利用 iOS 12 中內建的應用「捷徑」來實現。
動作連結: ofollow,noindex">點選下載 :link:
使用方式:點選連結匯入這個動作,執行它並從相簿選擇需要處理的 GIF 即可,最後的結果會儲存回手機相簿。

下面我來講解一下如何一步步製作並優化這個動作,以供參考。
動作解析
需求
明確需求是(功利性地)做任何東西之前的必須步驟。這裡我們的需求是:製作一個動作,它的輸入是從相簿選擇的一張 GIF,輸出則是一張帶有進度條的 GIF。
注意,最終版本的捷徑不需要自備素材,進度條的圖片已經被「內嵌」到了動作中。 具體思路可以看下面的製作和分析改進小節。
製作
過程原理在前文已經講明瞭。最為關鍵的步驟是:提取出 GIF 的每一幀,在每幀上疊加合適長度的進度條,然後再把處理後的幀拼接成新的 GIF。
iOS 12 內建的「捷徑」應用是 iOS 平臺著名效率應用 Workflow 的繼任者,裡面提供了許多基本的功能模組,使用者可自由組合這些模組來實現相對更復雜的需求。在本例中,捷徑提供了四個最為關鍵的動作:Get Frames From Image 、Resize Image、 Combine Images 與 Make GIF。
- Get Frames From Image 動作的輸入是一張 GIF,執行後可以提取出 GIF 的每一幀,得到一個包含了多張圖片的列表(list)。
- Resize Image 動作的輸入是一張圖片,它可以將這張圖片縮放到設定的尺寸。本例中就用它來製作進度條。你想到應該怎麼做了嗎?所謂進度條,其實就是一張細長的純色圖片,它可以由任何一張純色圖片製得:只要讓它的寬度是進度條長度,高度大約兩三個畫素即可。
- Combine Images 動作的輸入是一個圖片列表 4 ,它的作用是拼接列表中的圖片,縱向或橫向皆可,並輸出拼接後的圖片。本例中就用它來給每一幀加上進度條。
- Make GIF 的輸入是一個圖片列表,它可以將這個列表中的圖片合成為一個 GIF。
不難畫出完整的 流程圖:

分析與改進
按照以上流程圖製作的動作固然可以使用,但是有一個很大的缺點:它要求使用者相簿中有一張純色圖片以備使用,這……太不優雅了。再說,如果你分享給你女朋友,她卻發現不能直接用還要有什麼純色圖,她哪裡去找什麼純色圖?你怎麼這麼敷衍?你是不是不愛她了?
一種解決思路是:執行前檢測使用者裝置上有沒有這個純色圖片,若有,那就繼續執行,若沒有,則利用 Get Contents of URL 臨時下載,並存儲備用。儲存的位置若可能的話,最好不要是相簿,以免汙染別人的照片庫。很自然的,iCloud Drive 是一個絕佳選擇。因此,流程圖的前半部分變成了:

好的,這次女朋友不會奪命三問了。但是等等……若是女朋友手機沒網或者提供這個純色圖片的伺服器掛了呢?雖然確實不是你的問題,但是女朋友仍然有可能遷怒於你,害怕。我們要把隱患扼殺在搖籃中。
究其原因,一切都是因為這個動作需要引用外部的資源:一張純色圖。如果它能直接內建到動作裡就好了。能不能辦到?當然可以。
捷徑應用裡內建了一個動作: Base64 Encode ,有程式設計經驗的同學應該就明白了。簡單地說,Base64 1 可以把一個二進位制檔案轉換為一串純文字,也能從文字中解碼出原來的檔案。既然動作不能儲存圖片檔案,但儲存文字卻是可以的呀!因此在最頂部放一個 text 塊,裡面填寫上純色圖的 Base64 編碼,執行時再從這裡解碼得到純色圖片即可。應該說到這裡,這個動作完成度算是比較高了。

再來看看帶進度條的 GIF:

除了本文的動作,其他需要圖片素材的捷徑(比如 帶殼截圖 )也可以利用 Base64 編碼來儲存圖片,讓動作更加簡潔。
延伸
動作的製作與優化上面就聊完了。這部分是一些額外的討論和拋磚引玉的內容。
Add to Variable
在前面的流程圖中,我很自然地使用了 Add to Variable 這個動作,來臨時儲存需要拼接的進度條和某幀。捷徑應用中對這個步驟的簡介是:
Appends this action's input to the specified variable, creating the variable if it does not exsist.This allows you to make a variable hold multiple items.
也就是,將這個步驟的輸入追加到某個變數的末尾。值得注意的是,並不 與某個變數合併 ,而是 追加 。舉例來說,如果你把一段文字通過這個步驟 add 到另一個變數(也是一段文字)後,並不會得到一段包含了這二者的文字,而是得到了一個「列表」,裡面分別包含了兩段文字。
聽上去很繞。但我們只需要知道,這個步驟的輸出一般會是一個列表。Add to Variable 這個步驟的作用可歸結於 暫存多個變數,以待後續處理 。為什麼這麼說?因為「列表」其實是一種特殊的變數——儲存變數的變數。
暫存變數
以前使用過這個應用的玩家可能知道,應用中有一個叫做 List 的步驟,可以在執行過程中儲存幾個變數以供後續處理。在本例中,迴圈中第一次 Set Variable 和 後面 Add to Variable 然後拼接為新的一幀這一段完全可以這麼改寫:

那麼 Add to Variable 還有什麼存在的意義?
你是否注意到,雖然 List 這個動作在這裡的效果要比 Add to Variable 強,使得動作看起來更簡潔,但它有一個特點:容量是固定的 2 。在製作這個動作時只為它預留了兩個位置,一個用來放進度條,一個用來放圖片幀,那就不能再執行過程中儲存更多的東西了。
當需要暫存的內容個數不定時,List 動作就力不從心了,這時就輪到 Add to Variable 上場。不論有兩個、五個還是十個一百個變數,Add to Variable 都能老老實實地幫你存好。本文中要處理的 GIF 幀數不一,因此即使經過上圖的改寫,與進度條拼接後的幀仍然要使用 Add to Variable 暫存最後再合成 GIF 3 。
值得注意的是,列表中的資料型別無需是相同的,你可以把圖片、音樂、文字等等內容新增到一個 List 裡。暫存多個變數的部分至此結束。
以待後續處理
不論使用 List 還是 Add to Variable,你現在得到一個列表了。你能想到對它進行什麼操作?抽象地說,操作可分為對列表中的所有元素處理,或者對其中的某些進行處理。本文中拼接列表中的圖片屬於前者。
看一個屬於後者的例子。所謂「對某些進行處理」,無非是使用者手動選擇某些,或者自動過濾出某些。少數派 Shortcuts Gallery 中有個 影評日記 的動作就是就是這樣的。這個動作比較複雜,我不具體介紹裡面的內容,它的流程是這樣的:從豆瓣根據某個關鍵字搜尋電影,獲得前 5 個搜尋結果,提取每個搜尋結果的重要資訊合成一個 List,供使用者選擇,再根據選擇的結果從豆瓣請求更為詳細的資訊然後進行後續處理,有興趣請下載影評日記這個動作看看。
這裡有一個坑
回到上面那個流程圖,若是把迴圈中的第一個 Set Variable 也改成 Add to Variable,會發生什麼事情?大家可以試一試。
結果就完全不對了,但看起來沒有什麼邏輯問題啊?就是新增到 temp 變數嘛,雖然迴圈開始時沒有這個變數,但蘋果的文件也說了,沒有 temp 變數時會自動建立的。
這裡的問題在於,Shortcuts 中的變數是全域性有效的。因此在下一次迴圈開始時,上一次的迴圈的 temp 變數仍然是有效的,Add to Variable 是在上一次執行結束時 temp 變數上追加,自然就不對了。原動作中的 Set Variable 相當於是清空了 temp 變數,然後把它的值設為預定的值,如果這個位置非要使用 Add to Variable 的話也不是不可以,那麼就需要在每個迴圈末尾新增兩步:Nothing→Set Variable(temp),這相當於手動清空了 temp 變數。
Combine Images
前文提到,Combine Images 動作的作用是把一個列表裡的圖片拼接起來。動作上有三個選項:Mode,拼接方式,可選並列或者網格;Direction,方向,可選水平或者豎直,當 Mode 是網格時該選項無效;Spacing,間隔,即拼接時兩張圖片之間的距離。
本文中,Combine Images 的輸入是一個圖片 list,但我在腳註裡說,不一定非得如此。你可以試試在一個 list 中放一段文字和一張圖片,然後把這個 list 傳給 Combine Images,看看會出現什麼結果?
沒錯,動作會將文字轉換為圖片然後把它們拼接起來,對習慣了嚴格宣告資料型別的程式員來說這看起來是很奇妙的,雖然最終的效果可能不是很好(尺寸會變得很奇怪)。
關於這一點在蘋果的 Shortcuts 文件 中有詳細說明:
When an action expects one type of content and you pass it another type of content, the Content Graph automatically converts that content to the appropriate type.
也就是「自動型別轉換」。
「捷徑」應用的前身是 Workflow,可見這個開發團隊為了使任何人都能順暢地使用這個 App 在背後做了多少工作。要知道,對資料型別、程式程式碼都一無所知的使用者會製作出無數千奇百怪、奇思妙想的動作,要考慮到所有的意外是極為困難的,還不能簡單地報錯、丟擲異常,因為這樣太容易「勸退」了。正是開發團隊在背後做的工作使我在使用這個應用時屢屢覺得驚喜,這讓我想起了小時候偶得一套螺絲刀,才發現原來我僅靠自己的力量就能拆掉那麼多東西(誤),那是一種發自內心的喜悅。
本文也發表於我的部落格: 使用「捷徑」給 GIF 加上進度條 - 熊貓小A的部落格
> 最簡單又易懂的捷徑教程,就在 捷徑:由淺入深完全指南 :black_joker: