您以資料(而非產生的圖片)來建立商店截圖集。每個圖塊都是一個 HTML Canvas,包含多個圖層:背景、標題、帶有真實捕捉畫面的裝置外框,以及從畫面中以 3D 效果凸顯的跳出區塊(捕捉畫面的裁剪或向量說明方塊)。每個字串都存在 locales/<locale>.json 中,因此新語言只需要一個 JSON 檔案加上捕捉畫面,而且渲染是確定性的:相同的輸入會產生相同的像素。
$S 是此 SKILL.md 所在資料夾,$PY 是 $S/.venv/bin/python。如果缺少虛擬環境,請建立一次:
python3 -m venv $S/.venv && $S/.venv/bin/pip install -r $S/requirements.txt && $S/.venv/bin/playwright install chromium
管線:
1 優勢 -> 2 投影片 -> 3 capture.json -> 4 捕捉 (模擬器) -> 5 合成
-> 6 背景 -> 7 set.json + locales -> 8 渲染 -> 9 審查 -> (更多語言、裝置)
儲存庫中的 $S/../../examples/demo/ 是一個完整的專案(虛構應用程式,兩種語言,五種裝置)。在建立您自己的 set.json 之前,請先閱讀該專案的內容。
專案佈局
<project>/
capture.json 應用程式、捕捉裝置、要捕捉的畫面
set.json 所有語言共用的合成設定
locales/<locale>.json 所有字串:動詞、副標題、說明文字
sim/<locale>/<slide>.png 手機捕捉畫面(每個捕捉集一個資料夾)
sim-tablet/ sim-duo/ sim-android/
fonts/ assets/ 選用字型、背景圖片、道具
out/<locale>/<device>/ 僅限商店檔案
out/_sheets/ 每個語言和裝置的聯絡表
out/_captures/ 捕捉後每個語言的聯絡表
建議在應用程式儲存庫根目錄建立 store-screenshots/。將 out/ 和 .cache/ 加入其 .gitignore。
1. 優勢
閱讀程式碼庫:導覽(分頁、路徑、畫面)、上線文案、付費牆文案、README、現有商店列表。列出使用者會得到的 5 到 8 項好處,按它們對促進安裝的影響程度排序。每個投影片一個好處。
每個投影片的標題是一個全大寫的簡短動詞加上最多兩行的副標題:PLAN / YOUR WEEK IN MINUTES、SAVE / ON EVERY SHOP。動詞命名好處,而非功能。連同證明每個好處的畫面一起顯示列表,並獲得同意。
2. 投影片
ID 為 NN-name;數字是商店中的位置。為每個投影片選擇畫面,並記下其需要的狀態(已登入、存在資料、工作表開啟)。將圖片密集的畫面傾斜,保持資料密集的畫面方正。
3. capture.json
從 $S/templates/capture.json 開始。
| 鍵 | 意義 |
|---|---|
app.ios | bundleId,以及 app(模擬器 .app 建置的路徑)或 copyFrom(已安裝應用程式的模擬器 UDID) |
app.android | package,選用 activity,選用要安裝的 apk |
devices.<key> | platform ios 或 android,dir(捕捉資料夾),iOS:name(專用模擬器,由 setup 建立)和 type(xcrun simctl list devicetypes),選用 runtime;Android:avd(emulator -list-avds) |
| 裝置延伸 | appearance (light)、locale(覆蓋下方的模式)、displaySize (2853x2007 選取 iPhone Duo 內部顯示器)、rotate(度數,用於捕捉時側向的畫面) |
locale | 每個平台不同。iOS reboot(預設,最可靠)、relaunch(不重新啟動,保持可折疊裝置的姿勢)或 launchArgs。Android app(預設,Android 13+ 上的每個應用程式語言,需要應用程式的 localeConfig)或 system(需要具有 root 權限的模擬器映像) |
launch | 啟動後 wait 秒數,選用 url 以開啟而非啟動(Expo 開發用戶端 URL),選用 steps |
screens | {id, steps, ready, after, devices, copyFrom} 列表,加上 byDevice / byLocale 區塊,會合併到畫面上 |
步驟依序執行,每個步驟會等待(wait,預設 1 到 2 秒):
| 步驟 | 效果 |
|---|---|
{"open": "myapp://route"} | 深層連結。建議使用此方法而非點擊:它在每個裝置和平台上都有效 |
{"tap": {"label": "Save"}} | 點擊具有該無障礙標籤的元素(或 {"id": ...}),先精確匹配,再子字串匹配 |
{"tap": {"at": [0.5, 0.9]}} | 點擊螢幕的某個分數位置 |
{"tap": {...}, "until": {"label": "Details"}} | 再次點擊(最多 3 次),直到該元素出現;用於仍在穩定狀態的畫面 |
{"waitFor": {"label": "Done", "timeout": 20}} | 等待直到元素出現在畫面上,用於載入或動畫中的畫面 |
{"type": "pasta"}, {"key": "enter"}, {"key": "back"} | 文字和按鍵 |
{"swipe": [0.5, 0.8, 0.5, 0.3]} | 滑動,使用分數 |
{"launch": true} | 重新啟動應用程式 |
ready: {"region": [x, y, w, h], "minStd": 20, "tries": 5, "delay": 3} 在該區域為純色時(骨架行、旋轉指示器、仍在載入的圖片)會重新拍攝。iPhone 捕捉畫面會在模擬器將動態島渲染進去時自動重新拍攝。
copyFrom: "01-home" 在該裝置上重用另一個捕捉畫面(例如平板在分割畫面中顯示兩個螢幕)。devices: ["phone"] 將畫面限制在某些裝置。
capture.py PROJECT describe --device phone 會列印可點擊的元素及其標籤和框架。
4. 捕捉
$PY $S/capture.py <project> doctor # 工具、裝置、應用程式 ID
$PY $S/capture.py <project> setup # 建立/啟動專用模擬器、啟動 AVD、安裝應用程式
$PY $S/capture.py <project> run --locale en-US # 每個裝置和畫面
$PY $S/capture.py <project> run --locale en-US --device phone --screen 03-search
- 捕捉裝置是專用的:iOS 模擬器按名稱建立(
Capture iPhone...),Android 使用 capture.json 中命名的 AVD。切勿將 capture.json 指向使用者工作的模擬器,並在啟動裝置或切換其語言前先詢問。 setup之後,應用程式通常需要示範狀態:登入示範帳戶、種子資料、關閉首次執行對話(儲存密碼、通知、評論提示)。每個裝置執行一次;狀態在語言切換後仍會保留。向使用者詢問示範憑證,切勿將它們寫入 capture.json。- 顯示除錯疊加層(開發選單按鈕、Toast)的開發建置需要在捕捉工作階段中隱藏這些內容。在本機將它們修補掉,且不要提交修補。
- 逐圖塊審查
out/_captures/<locale>.jpg:沒有其他語言的文字、沒有對話框、Toast、旋轉指示器或空白狀態。修正資料或步驟,然後重新執行該畫面。渲染器會如實顯示捕捉畫面;沒有任何東西會覆蓋錯誤。 - iPhone Duo:先在模擬器或 DeviceHub 中展開模擬器並將其設為橫向;simctl 無法折疊或旋轉。使用深層連結(HID 點擊可能無法到達內部顯示器)、
"locale": "relaunch"和"displaySize": "2853x2007"。
跨平台應用程式(React Native、Flutter)可以跳過 Android 捕捉:Android 圖塊會重用 iPhone 和 iPad 的捕捉畫面,這也是預設裝置的行為。原生 Android 應用程式會將自己的畫面捕捉到 sim-android/,請參閱第 10 節。
5. 合成(使用者在任何渲染前批准)
以 ASCII 草繪每個投影片:標題、裝置位置和傾斜度、哪個元素跳出以及它落在哪裡。獲得同意後,再編寫 set.json。
+------------------------------+
| WATER | 動詞,一行
| EVERY PLANT RIGHT | 副標題,最多 2 行
| ON TIME |
| .--------------. |
| [* 3 plants today] | 說明方塊,跳出,向左突出
| | Today ... | | 裝置傾斜 y 15, x 4
+------------------------------+
6. 背景
每個集合一個背景,所有語言和裝置共用。可以是 CSS("background": {"css": "radial-gradient(...), #1f4a37"},示範使用此方法)或圖片({"image": "assets/bg.png"},background-size cover)。圖片產生器很適合製作柔和背景;提示產生一個無縫的 9:16 工作室背景,使用品牌色並搭配柔和光線,並明確說明:沒有物件、沒有文字、沒有裝置、沒有形狀。產生兩個,由使用者選擇。道具(貼紙、產品照片)是帶有 Alpha 通道的 PNG,放置在每個投影片上。
7. set.json 和 locales
使用 measure.py(而非目測)測量每個矩形在捕捉像素中的尺寸:
$PY $S/measure.py sim/en-US/05-fill.png --at 700,790 # 填充按鈕、晶片、藥丸
$PY $S/measure.py sim/en-US/01-plan.png --ink 40,415,700,75 # 純色區域上的文字邊界
$PY $S/measure.py sim/en-US/02-cook.png --grid 440,1510,340,110 # 暫存目錄中的放大網格圖片
| 鍵 | 意義 |
|---|---|
locales、devices | 完整渲染涵蓋的範圍;裝置 ID 來自 $S/devices/devices.json |
background | {"image": ...} 或 {"css": ...},選用 position |
fonts | {"family": "fonts/File.otf"},路徑相對於專案;預設 display 是隨附的 Archivo Black |
headline | font、descFont、verbColor、descColor、shadow、verbTracking、descTracking、descWordSpacing |
capture | platform、statusBarBand(混合掉的捕捉行,iOS 預設 [66, 128])、sets({"tablet": {"dir": "sim-tablet", "statusBarBand": [12, 56]}})、devices({"android-phone": "android"} 將裝置指向另一個捕捉集) |
style | gloss、deviceShadow、thicknessRatio(側壁深度)、sideLayers |
statusBar | 渲染器繪製的狀態列 time、color |
layout | 每個裝置 ID,覆蓋 devices.json 的佈局(deviceTop、deviceWidth、verbMax ...) |
baseline | 現有集合的 {device: {locale: folder}},用於 --compare |
slides | 列表,如下 |
投影片:id、sim(如果非 <id>.png 的捕捉檔案)、tilt {x, y, z} 度數、offset {x, y} Canvas 分數、layout、breakouts、props(src、x、y、w Canvas 分數、rotate、shadow、z:front 或 back)。
跳出區塊:
| 鍵 | 意義 |
|---|---|
type | crop:捕捉畫面本身的像素;callout:帶有地區文字向量的藥丸形狀 |
rect 或 anchor | 捕捉像素中元素來源的 [x, y, w, h] |
at 或 dx/dy | 著陸位置(at = 捕捉像素中新的左上角)或偏移 |
scale、lift | 大小和 Z 軸抬起高度(像素,預設 80) |
vacate、vacatePad | 用周圍顏色填滿原始位置(已移動,非複製) |
radius、shadow、origin | 捕捉像素中的圓角半徑、陰影強度、變形原點 |
text、icon、style | 說明方塊:locale 的 callouts 中的鍵、tag 或 sparkles,以及 font、size、bg、fg、padX、padY、gap、radius、weight、tracking、iconScale、iconColor |
slides 內部的任何值都可以根據裝置和語言而變化:{"byDevice": {"phone": ..., "tablet": ..., "default": ...}}(鍵:裝置 ID、捕捉集、default)和 {"byLocale": {"de-DE": ..., "default": ...}},可以任意嵌套。矩形幾乎總是會根據語言而變化,因為同一個晶片在德文中更寬。
裁剪在大約 1.7 倍以內對大型元素保持銳利。小的晶片和文字會變成說明方塊,它們是向量,在任何縮放下都保持銳利。
地區檔案:
{
"captures": "en-US",
"dir": "ltr",
"fonts": {"display": "fonts/NotoSansJP-Black.otf"},
"slides": {
"01-plan": {"verb": "Plan", "desc": "Your week in minutes", "callouts": {"savings": "approx. €7.81 saved"}}
}
}
只需要 slides。captures 讓一個地區重用另一個地區的捕捉畫面(例如 en-GB 使用 en-US 的畫面)。headline 和 statusBar 覆蓋 set.json 中的設定。Archivo Black 僅涵蓋拉丁字母;斯拉夫字母、希臘字母、CJK、阿拉伯字母和希伯來字母需要 fonts 覆蓋,RTL 需要 "dir": "rtl"。
8. 渲染
$PY $S/render.py <project> # 所有內容
$PY $S/render.py <project> --locale de-DE --device iphone-67 --slide 01-plan
$PY $S/render.py <project> --compare # 基準線 vs 新頁面
標題大小在同一個語言和裝置的所有投影片中共享,因此整個集合讀起來是一致的。當捕捉畫面的長寬比與裝置的螢幕開口不符時,渲染器會停止。
9. 審查
開啟 out/_sheets/<locale>-<device>.jpg 並放大檢視每個跳出區塊。列印為 CHECK 的行會指出問題(標題尺寸過小、重疊)。標題尺寸過小表示文案太長:請縮短文案,不要降低限制。向使用者顯示頁面。
10. 更多語言和裝置
新增語言:
$PY $S/add_locale.py <project> es-ES init --from en-US # 地區檔案、byLocale 佔位符、資料夾
$PY $S/capture.py <project> run --locale es-ES # 捕捉
# 翻譯 locales/es-ES.json:商店文案、簡短動詞、與捕捉畫面匹配的說明方塊
$PY $S/add_locale.py <project> es-ES measure --from en-US # 重新尋找矩形,審查並排圖片
$PY $S/add_locale.py <project> es-ES measure --from en-US --write
$PY $S/add_locale.py <project> es-ES check # 在渲染前應為 0 錯誤
$PY $S/render.py <project> --locale es-ES
區域變體(en-GB、de-AT、fr-CA):使用 init --share-captures,然後只更改文字。
$S/devices/devices.json 中的裝置:
| id | canvas | 讀取來源 | 商店位置 |
|---|---|---|---|
iphone-67 | 1290x2796 | phone (1206x2622) | ASC APP_IPHONE_67 |
android-phone | 1080x1920 | phone | Play 手機截圖 |
ipad-13 | 2064x2752 | tablet (2064x2752) | ASC APP_IPAD_PRO_3GEN_129 |
android-tablet | 1792x2400 | tablet | Play 7" 和 10" 平板 |
iphone-duo | 2853x2007 | duo (2853x2007) | ASC iPhone Duo |
這些框架是使用 make_frame.py 繪製的通用圖形,可自由重新分發。如果捕捉尺寸與現有框架不符,則會建立一個新的框架:
# 原生 Android 捕捉,1080x2400,放入 sim-android/
$PY $S/make_frame.py --style android --screen 1080x2400 --device android-phone --captures android
# 然後在 set.json 中:"capture": {"sets": {"android": {"dir": "sim-android", "statusBarBand": [30, 105]}}}
樣式:ios、android、tablet、landscape。--canvas WxH 為新的裝置 ID 設定商店尺寸。在真實捕捉畫面上測量 statusBarBand:狀態列的行數加上一些留白。