play-store-screenshots
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePlay Store Screenshots Generator
Play Store截图生成器
Overview & Core Philosophy
概述与核心理念
This skill generates advertising-focused Google Play Store screenshots using Next.js and . The fundamental principle: screenshots are advertisements, not documentation. Each slide communicates a single outcome, feeling, or solution — not a UI tour.
html-to-imageUnlike the Apple App Store, Play Store also requires a Feature Graphic (1024×500px) — a landscape banner displayed prominently on your app's store listing page. This skill treats it as a first-class asset alongside phone screenshots.
本Skill基于Next.js和生成以广告宣传为导向的Google Play Store截图。核心原则:截图是广告,而非文档。每一页只传递一个价值点、使用感受或解决方案,而不是对UI功能的遍历介绍。
html-to-image与Apple App Store不同,Play Store还要求提交Feature Graphic(特色图,1024×500px)——这是展示在应用商店详情页顶部的横版横幅,本Skill将其与手机截图同等对待,作为核心输出资产。
Discovery Phase
需求确认阶段
The goal is to start generating as fast as possible without making wrong assumptions that waste the user's time. The approach: ask what you must, derive what you can, always show your work and confirm before proceeding.
目标是尽可能快速启动生成流程,同时避免做出错误假设浪费用户时间。方法:必填信息主动询问,可推导信息自行提取,推进前同步所有结论并确认。
Step 1 — Ask first, look second
步骤1 — 先询问,后排查
Before looking at any files, ask the one question that cannot be derived:
"What are your top 3–6 features? For each, briefly describe what problem it solves for your users."
This is a marketing decision — no file or screenshot can answer it. The user knows their app's value proposition.
If the user gives feature names without the problem angle (e.g. "offline mode, dark theme, favourites"), follow up:
"For each one — what would a user find frustrating if that feature didn't exist?"
Do not move to Step 2 until you have this.
在查看任何文件前,先询问唯一一个无法推导的问题:
"你的应用Top 3-6个核心功能是什么?请针对每个功能简要描述它为用户解决了什么痛点。"
这是营销决策,任何文件或截图都无法给出答案,只有用户清楚自己应用的价值主张。
如果用户只给出了功能名称,没有说明痛点(比如"离线模式、深色模式、收藏功能"),需要追问:
"针对每个功能,如果没有它用户会遇到什么困扰?"
拿到答案前不要进入步骤2。
Step 2 — Scan and find assets
步骤2 — 扫描并查找素材
Once you have the features, scan the current directory for assets. Look for:
- Screenshots: /
.pngfiles in the current folder or a.jpgsubfolderscreenshots/ - App icon: check standard Android paths first:
If not an Android project, look for any file named
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png app/src/main/res/mipmap-xxhdpi/ic_launcher.png,icon*.png, orlogo*.pngapp-icon.*
If you find exactly what you need — list the files and say:
"I found these screenshots: [list]. I'll use these. Is that correct?"
If you find multiple candidates and can't tell which is right — show them and ask:
"I found several PNG files. Which ones are the app screenshots to use?"
If you find nothing — ask directly:
"I couldn't find any screenshots in this directory. Where are your Android app screenshots saved?"
Never guess silently when there is ambiguity. One wrong file wastes the entire run.
拿到功能信息后,扫描当前目录查找素材,寻找:
- 截图:当前目录或子目录下的
screenshots//.png文件.jpg - 应用图标:优先检查标准Android路径:
如果不是Android项目,查找所有名为
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png app/src/main/res/mipmap-xxhdpi/ic_launcher.png、icon*.png或logo*.png的文件app-icon.*
如果完全找到所需素材:列出文件并告知:
"我找到了这些截图:[列表],我将使用这些素材,是否正确?"
如果找到多个候选无法判断:展示出来询问:
"我找到了多个PNG文件,哪些是要使用的应用截图?"
如果没有找到任何素材:直接询问:
"我在当前目录中没有找到任何截图,你的Android应用截图保存在哪里?"
存在歧义时绝对不要私下猜测,一个错误的文件就会导致整个生成流程无效。
Step 3 — Derive visual settings from the screenshots
步骤3 — 从截图推导视觉设置
Once you have the screenshots, analyze them visually and derive the following. Present your findings as a short confirmation block before writing any code:
Here's what I picked up from your screenshots — let me know if anything looks off:
App name: Aarti Mandir
Primary: #C9822A (toolbar orange)
Background: #FFF8F0 (warm cream)
Text: #2D1A00 (dark brown)
Accent: #C9822A
Theme: editorial (warm/earthy tones)
Font: Inter (default — let me know if you use a specific font)
Frame: white (light app)
Slides: 5Only proceed once the user confirms or corrects this. A 10-second confirmation here prevents regenerating everything because a color was wrong.
How to derive each value:
| Value | How to derive |
|---|---|
| App name | Read it from the screenshots (toolbar, splash screen, icon label) |
| Primary color | Dominant UI color — toolbar, header, active tab, filled buttons |
| Background color | Color behind most of the screen content |
| Text color | Infer from background — dark bg → |
| Accent color | CTA buttons, highlights, toggles, active states |
| Secondary color | Gradient partner — shift primary hue 15–20 degrees, or use a visible secondary UI color |
| Theme | Dark bg → |
| Font | Default Inter unless app screenshots show a clearly distinctive typeface |
| Frame color | Dark app (dark toolbar + bg) → |
| Slide count | Default 5 |
If you genuinely cannot determine a value from the screenshots — ask. Do not guess.
"I can't clearly make out your primary brand color from the screenshots. What hex color is your main brand color?"
拿到截图后,可视化分析并推导以下参数。在编写任何代码前,先把推导结果整理成简短的确认块发送给用户:
我从你的截图中提取到了以下信息,如果有不对的地方请告知:
应用名称: Aarti Mandir
主色调: #C9822A (工具栏橙色)
背景色: #FFF8F0 (暖奶油色)
文本色: #2D1A00 (深棕色)
强调色: #C9822A
主题: editorial (暖/大地色调)
字体: Inter (默认 — 如果你使用特定字体请告知)
样机边框: white (浅色应用)
截图页数: 5只有用户确认或修正后才能继续推进,10秒的确认可以避免因为颜色错误需要重新生成所有内容的问题。
每个参数的推导方法:
| 参数 | 推导方式 |
|---|---|
| 应用名称 | 从截图中读取(工具栏、启动页、图标标签) |
| 主色调 | 核心UI颜色 — 工具栏、头部、激活标签、填充按钮 |
| 背景色 | 大部分屏幕内容背后的颜色 |
| 文本色 | 根据背景色推断 — 深色背景→ |
| 强调色 | CTA按钮、高亮、开关、激活状态颜色 |
| 辅助色 | 渐变搭配色 — 将主色色相偏移15-20度,或使用可见的次要UI颜色 |
| 主题 | 深色背景→ |
| 字体 | 默认Inter,除非截图中有明显独特的字体 |
| 样机边框颜色 | 深色应用(深色工具栏+背景)→ |
| 截图页数 | 默认5 |
如果确实无法从截图中确定某个参数:直接询问,不要猜测。
"我无法从截图中清晰识别你的主品牌色,请问你的主品牌色十六进制代码是什么?"
Step 4 — Confirm, then build
步骤4 — 确认后构建
After the user confirms the summary in Step 3, proceed to generate and . Do not ask any more questions unless something unexpected comes up.
config.jsindex.htmlSilent defaults (never ask about these unless the user raises them):
- :
tablet.enabledfalse - :
locales["en"] - :
featureGraphic.style"gradient" - :
featureGraphic.subtext"Available on Google Play"
用户确认步骤3的总结信息后,继续生成和。除非遇到意外问题,不要再询问其他问题。
config.jsindex.html静默默认配置(除非用户提出,否则永远不要询问这些配置):
- :
tablet.enabledfalse - :
locales["en"] - :
featureGraphic.style"gradient" - :
featureGraphic.subtext"Available on Google Play"
What to ask vs. what to derive
询问 vs 推导边界
| Input | Approach |
|---|---|
| Features + user problems | Always ask — cannot be derived |
| Screenshot paths | Scan → confirm if found, ask if not found or ambiguous |
| App icon path | Scan standard paths → confirm if found, ask if not |
| App name | Read from screenshots → confirm in summary |
| Brand colors | Extract visually → confirm in summary, ask if unclear |
| Theme | Infer from bg color → confirm in summary |
| Font | Default Inter → confirm in summary, note if app uses something distinctive |
| Device frame color | Infer from app tone → confirm in summary |
| Slide count | Default 5 → state in summary, user can override |
| Tablet / locale / landscape | Default off/en/portrait → never ask, only activate if user raises it |
| 输入 | 处理方式 |
|---|---|
| 功能+用户痛点 | 必须询问 — 无法推导 |
| 截图路径 | 扫描→找到则确认,找不到或有歧义则询问 |
| 应用图标路径 | 扫描标准路径→找到则确认,找不到则询问 |
| 应用名称 | 从截图读取→在总结中确认 |
| 品牌颜色 | 可视化提取→在总结中确认,不清晰则询问 |
| 主题 | 从背景色推断→在总结中确认 |
| 字体 | 默认Inter→在总结中确认,如果应用使用特殊字体则标注 |
| 设备边框颜色 | 从应用风格推断→在总结中确认 |
| 截图页数 | 默认5→在总结中说明,用户可修改 |
| 平板/多语言/横版 | 默认关闭/英文/竖版→永远不要询问,只有用户提出才启用 |
Project Architecture
项目架构
This skill generates two files in a new folder. No framework, no , no build step.
npm installplay-store-screenshots/
├── index.html ← rendering engine, export logic, overview mode (don't edit)
├── config.js ← ALL customization: copy, colors, slides (edit this)
└── screenshots/ ← user's raw Android app screenshots + app icon
├── app-icon.png
├── screen1.png
└── screen2.pngThe AI fills in . reads from it and renders everything.
config.jsindex.htmlTo use: serve the folder with a one-line local server (required for loading local images), open in Chrome, click Export All.
bash
undefined本Skill会在新文件夹中生成两个文件,无框架依赖、无需、无需构建步骤。
npm installplay-store-screenshots/
├── index.html ← 渲染引擎、导出逻辑、预览模式(无需编辑)
├── config.js ← 所有自定义配置:文案、颜色、截图页(编辑这个文件即可)
└── screenshots/ ← 用户的原始Android应用截图+应用图标
├── app-icon.png
├── screen1.png
└── screen2.pngAI负责填充,读取配置并渲染所有内容。
config.jsindex.html使用方法:用一行命令启动本地服务器(加载本地图片需要),在Chrome中打开,点击导出全部即可。
bash
undefinedPick any one of these — whatever is available:
任选一个你环境中可用的命令:
python3 -m http.server 8080
npx serve .
Then open `http://localhost:8080`.
> **Note on exported files:** Clicking "Export All" triggers browser downloads — files land in your system's **Downloads folder**. Rename and organize from there before uploading to Play Console.python3 -m http.server 8080
npx serve .
然后打开`http://localhost:8080`。
> **导出文件说明:** 点击"导出全部"会触发浏览器下载,文件会保存到系统的**下载文件夹**。上传到Play Console前可以重命名整理。Should you commit this project to git?
是否需要把这个项目提交到git?
No. The screenshot generator is a disposable tool — once you have the PNGs, the project has done its job. The only files worth keeping long-term are:
- The exported PNGs (upload to Play Console)
- if you want to regenerate or tweak later
config.js
Work in any temporary folder. Do not add it to your app's repository.
不需要。截图生成器是一次性工具,拿到导出的PNG后任务就完成了。值得长期保留的文件只有:
- 导出的PNG文件(上传到Play Console)
- (如果后续需要重新生成或调整)
config.js
可以在任何临时文件夹中工作,不要把它添加到应用的代码仓库中。
config.js Reference
config.js参考
config.jsA complete working example for a fictional app is available at in this skill directory — copy it as a starting point.
example-config.jsjavascript
const config = {
app: {
name: "Your App",
icon: "screenshots/app-icon.png",
},
brand: {
primary: "#6366F1", // dominant brand color
secondary: "#4F46E5", // gradient partner / secondary brand color
background: "#FFFFFF", // canvas background
surface: "#F4F4F8", // card / panel backgrounds
text: "#111111", // headline color
textMuted: "#666666", // caption / label color
accent: "#F59E0B", // highlight, call-to-action color
},
// "clean-light" | "dark-bold" | "vibrant" | "editorial"
theme: "clean-light",
device: {
color: "black", // "black" | "white" | "silver"
},
slides: [
{
id: "hero",
layout: "text-top",
screenshot: "screenshots/screen1.png",
label: "INTRODUCING",
headline: "Your app name.\nYour outcome.",
// Leave subtext empty on hero — let the visual breathe
},
{
id: "feature-one",
layout: "text-top",
screenshot: "screenshots/screen2.png",
label: "FEATURE NAME",
headline: "Short benefit\nstatement here.",
subtext: "One supporting sentence, maximum.",
},
// Add 2–8 slides total — all use layout: "text-top"
],
featureGraphic: {
headline: "The app headline goes here",
subtext: "Available on Google Play",
// "gradient" | "solid" | "screenshot-backed"
style: "gradient",
// Only used when style is "screenshot-backed"
screenshot: "screenshots/screen1.png",
},
// Optional: set enabled: true and add tablet screenshots to activate
tablet: {
enabled: false,
sizes: ["10-inch"], // "7-inch" | "10-inch" | both
},
// Add locale codes to generate localized sets: "de", "fr", "ar", "he", etc.
locales: ["en"],
};config.js本Skill目录下的提供了虚构应用的完整可运行示例,可以复制作为起点。
example-config.jsjavascript
const config = {
app: {
name: "Your App",
icon: "screenshots/app-icon.png",
},
brand: {
primary: "#6366F1", // 主品牌色
secondary: "#4F46E5", // 渐变辅助色/次品牌色
background: "#FFFFFF", // 画布背景色
surface: "#F4F4F8", // 卡片/面板背景色
text: "#111111", // 标题颜色
textMuted: "#666666", // 说明/标签颜色
accent: "#F59E0B", // 高亮、CTA按钮颜色
},
// "clean-light" | "dark-bold" | "vibrant" | "editorial"
theme: "clean-light",
device: {
color: "black", // "black" | "white" | "silver"
},
slides: [
{
id: "hero",
layout: "text-top",
screenshot: "screenshots/screen1.png",
label: "INTRODUCING",
headline: "Your app name.\nYour outcome.",
// 首屏不要填写子文案,给视觉留出呼吸空间
},
{
id: "feature-one",
layout: "text-top",
screenshot: "screenshots/screen2.png",
label: "FEATURE NAME",
headline: "Short benefit\nstatement here.",
subtext: "One supporting sentence, maximum.",
},
// 总共添加2-8个截图页,都使用layout: "text-top"
],
featureGraphic: {
headline: "The app headline goes here",
subtext: "Available on Google Play",
// "gradient" | "solid" | "screenshot-backed"
style: "gradient",
// 仅当style为"screenshot-backed"时使用
screenshot: "screenshots/screen1.png",
},
// 可选:设置enabled: true并添加平板截图即可启用
tablet: {
enabled: false,
sizes: ["10-inch"], // "7-inch" | "10-inch" | 两者都选
},
// 添加语言代码生成多语言版本:"de", "fr", "ar", "he"等
locales: ["en"],
};Multi-locale copy
多语言文案
When contains more than one entry, change string values to locale maps:
localesjavascript
slides: [
{
id: "hero",
layout: "text-top",
screenshot: { en: "screenshots/en/screen1.png", de: "screenshots/de/screen1.png" },
label: { en: "INTRODUCING", de: "NEU" },
headline: { en: "Your app.\nYour way.", de: "Deine App.\nDein Weg." },
},
]当包含多个条目时,把字符串值改为语言映射:
localesjavascript
slides: [
{
id: "hero",
layout: "text-top",
screenshot: { en: "screenshots/en/screen1.png", de: "screenshots/de/screen1.png" },
label: { en: "INTRODUCING", de: "NEU" },
headline: { en: "Your app.\nYour way.", de: "Deine App.\nDein Weg." },
},
]Slide Layout
截图页布局
One layout. Used consistently.
统一使用同一种布局
Every phone slide uses the same composition: text at the top, large phone below. This is not a limitation — it is the correct pattern for portrait Play Store screenshots. It is what works.
┌─────────────────────┐
│ App Name │ ← small, top padding ~6% of canvas height
│ │
│ LABEL │ ← all-caps label
│ Big headline │ ← largest text on the slide
│ here. │
│ Subtext sentence. │ ← optional, smaller
│ │
│ ┌───────────────┐ │
│ │ │ │
│ │ [screenshot] │ │ ← phone, large, centered
│ │ │ │
│ │ │ │ ← bottom of phone crops off canvas (intentional)
└──┴───────────────┴──┘Standard phone sizing — use these exact values:
| Property | Value |
|---|---|
| Phone width | |
| Phone height | |
| Phone left | |
| Phone top | |
| Bottom crop | ~25% of phone height clips off canvas — intentional, adds depth |
The phone bottom extending past the canvas edge is intentional and desirable — it grounds the phone visually and removes the awkward empty space that appears when the whole frame is visible.
Text block positioning:
The text container spans with . Content is vertically centered within it. The phone starts at , leaving a ~2% breathing gap between the text block bottom and the phone top edge — this prevents the label chip or last headline line from touching the device frame.
height: PHONE_H * 0.32overflow: hiddenPHONE_H * 0.34| Element | Position |
|---|---|
| App name / brand | |
| Label | |
| Headline | Below label, centered, |
| Subtext | Below headline, centered, |
Keep the entire text block within the top 32% of canvas height (the container enforces this with ). If text overflows, shorten the headline — do not push the phone lower.
overflow: hidden所有手机截图页使用相同的构图:顶部文本,下方大尺寸手机样机。这不是限制,而是竖版Play Store截图的最优范式,经过验证效果最好。
┌─────────────────────┐
│ 应用名称 │ ← 小号字体,顶部内边距约为画布高度的6%
│ │
│ 标签 │ ← 全大写标签
│ 大标题 │ ← 页面最大字号
│ 在这里 │
│ 子文案说明 │ ← 可选,小号字体
│ │
│ ┌───────────────┐ │
│ │ │ │
│ │ [截图内容] │ │ ← 手机样机,大尺寸,居中
│ │ │ │
│ │ │ │ ← 手机底部裁剪超出画布(有意设计)
└──┴───────────────┴──┘标准手机尺寸 — 严格使用以下数值:
| 属性 | 数值 |
|---|---|
| 手机宽度 | |
| 手机高度 | |
| 手机左侧位置 | |
| 手机顶部位置 | |
| 底部裁剪 | 约25%的手机高度裁剪超出画布 — 有意设计,增加层次感 |
手机底部延伸超出画布边缘是有意且理想的设计:它让手机在视觉上更接地气,避免整个边框都可见时出现的尴尬空白。
文本块定位:
文本容器高度为,设置,内容在容器内垂直居中。手机从位置开始,文本块底部和手机顶部边缘之间留出约2%的呼吸间隙,避免标签或标题最后一行碰到设备边框。
height: PHONE_H * 0.32overflow: hiddenPHONE_H * 0.34| 元素 | 位置 |
|---|---|
| 应用名称/品牌 | |
| 标签 | |
| 标题 | 标签下方,居中, |
| 子文案 | 标题下方,居中, |
确保整个文本块在画布高度的前32%以内(容器通过强制约束)。如果文本溢出,缩短标题,不要把手机位置往下移。
overflow: hiddenfeature-graphic
(feature graphic only — not a phone slide)
feature-graphicfeature-graphic
(仅特色图,不是手机截图页)
feature-graphicLandscape canvas (1024×500px). No device mockup. Two composition options:
- Icon-left: App icon (180×180) on left third, headline + subtext on right two thirds
- Icon-center: App icon centered at top, headline below, subtext below that
Background fills the full canvas using the chosen .
featureGraphic.style横版画布(1024×500px),无设备样机。两种构图选项:
- 图标居左: 应用图标(180×180)在左侧三分之一区域,标题+子文案在右侧三分之二区域
- 图标居中: 应用图标在顶部居中,标题在下方,子文案在标题下方
背景使用选择的填充整个画布。
featureGraphic.styleSupported layouts
支持的布局
The template implements exactly two layouts:
- — use this for most slides (described above)
text-top - — full-screen screenshot with a gradient overlay; text floats at the top
full-bleed
Any other value will render a red error box. Do not write slides with , , , or — they are not implemented.
layoutlayout: "text-bottom"layout: "split-screen"layout: "hero-left"layout: "hero-right"模板仅实现两种布局:
- — 大部分截图页使用这个布局(上文已说明)
text-top - — 全屏截图叠加渐变层,文本浮动在顶部
full-bleed
其他任何值都会渲染红色错误框。不要编写、、或的截图页,这些布局都没有实现。
layoutlayout: "text-bottom"layout: "split-screen"layout: "hero-left"layout: "hero-right"full-bleed
layout
full-bleedfull-bleed
布局
full-bleedThe screenshot fills the entire canvas (). A dark gradient fades from the top (~35% height) to transparent, sitting above the screenshot. Label and headline float over this gradient at the top, in white. No device mockup.
width:100%; height:100%; object-fit:coverUse this for one slide only — it works best when the app screenshot is visually compelling on its own. Never use it for screenshots showing dense UI (lists, settings). Avoid pairing consecutive slides.
full-bleed┌─────────────────────┐
│ LABEL │ ← white text over gradient
│ Big headline │
│ here. │
│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ ← gradient fade
│ │
│ [screenshot fills │
│ entire canvas] │
│ │
└─────────────────────┘截图填充整个画布()。深色渐变从顶部(约35%高度)过渡到透明,叠加在截图上方。标签和标题以白色浮动在渐变上方顶部,无设备样机。
width:100%; height:100%; object-fit:cover仅在一个截图页使用这个布局,当应用截图本身视觉效果非常好的时候效果最佳。永远不要用于显示密集UI的截图(列表、设置)。避免连续使用布局的截图页。
full-bleed┌─────────────────────┐
│ 标签 │ ← 渐变上方白色文本
│ 大标题 │
│ 在这里 │
│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ ← 渐变过渡
│ │
│ [截图填充 │
│ 整个画布] │
│ │
└─────────────────────┘Android Device Mockup (CSS-Only)
Android设备样机(纯CSS实现)
The Android phone frame is implemented in pure CSS — no PNG asset required. This avoids the fragile PNG-offset coupling where exact pixel values must match a specific image file.
Component:
<AndroidPhoneMockup>Props: (string path), ("black" | "white" | "silver"), (number px), (number px)
screenshotcolorwidthheightRendered structure and key proportions (all values derived from component / ):
widthheight┌──────────────────────┐ outer frame (corner radius: width*0.12)
│ ● │ punch-hole camera: width*0.028 diameter, centered, top height*0.018
│ ┌────────────────┐ │ screen: left/right inset width*0.030, top/bottom inset height*0.025
│ │ │ │
│ │ [screenshot] │ │
│ │ │ │
│ └────────────────┘ │
│ ─────── │ home indicator: width*0.30 wide, height*0.003 tall, bottom height*0.015
└──────────────────────┘| Property | Value |
|---|---|
| Outer corner radius | |
| Frame border thickness | |
| Screen left/right inset | |
| Screen top inset | |
| Screen bottom inset | |
| Camera diameter | |
| Camera center from top | |
| Home indicator width | |
| Home indicator height | |
| Home indicator from bottom | |
Frame colors by prop:
color- → frame
"black", border#1C1C1E, screen background#3A3A3C#000000 - → frame
"white", border#F2F2F7, screen background#D1D1D6#FFFFFF - → frame
"silver", border#8E8E93, screen background#636366#111111
Critical: Never use brand colors for the phone frame or border. The frame must always use one of the neutral color sets above. Using a brand color (e.g. the app's accent orange or purple) on the frame border looks amateurish and draws attention away from the screen content. The frame should recede visually, not compete with the UI.
The frame should also have a soft drop shadow to lift it off the background:
css
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.10);Adjust opacity based on theme: lighter shadow for light backgrounds, stronger for dark.
Side buttons (optional detail, improves realism):
- Power button: right side, top 38% from top, height 8% of total height, width 0.6% of width
- Volume up/down: left side, stacked at 32% and 42% from top, same dimensions
Android手机边框用纯CSS实现,无需PNG素材。这避免了PNG偏移耦合问题,无需让精确像素值匹配特定图片文件。
组件:
<AndroidPhoneMockup>属性:(字符串路径)、("black" | "white" | "silver")、(像素数值)、(像素数值)
screenshotcolorwidthheight渲染结构和关键比例(所有数值从组件/推导):
widthheight┌──────────────────────┐ 外边框(圆角:width*0.12)
│ ● │ 打孔摄像头:直径width*0.028,居中,顶部距离height*0.018
│ ┌────────────────┐ │ 屏幕:左右内边距width*0.030,上下内边距height*0.025
│ │ │ │
│ │ [截图内容] │ │
│ │ │ │
│ └────────────────┘ │
│ ─────── │ 主页指示器:宽度width*0.30,高度height*0.003,底部距离height*0.015
└──────────────────────┘| 属性 | 数值 |
|---|---|
| 外边框圆角 | |
| 边框厚度 | |
| 屏幕左右内边距 | |
| 屏幕顶部内边距 | |
| 屏幕底部内边距 | |
| 摄像头直径 | |
| 摄像头中心距顶部距离 | |
| 主页指示器宽度 | |
| 主页指示器高度 | |
| 主页指示器距底部距离 | |
不同属性对应的边框颜色:
color- → 边框
"black",描边#1C1C1E,屏幕背景#3A3A3C#000000 - → 边框
"white",描边#F2F2F7,屏幕背景#D1D1D6#FFFFFF - → 边框
"silver",描边#8E8E93,屏幕背景#636366#111111
重要: 永远不要用品牌色作为手机边框或描边颜色。边框必须始终使用上述中性色集。在边框描边上使用品牌色(比如应用的橙色或紫色强调色)会显得非常不专业,还会分散用户对屏幕内容的注意力。边框应该在视觉上后退,不要和UI竞争注意力。
边框还应该添加柔和的阴影,让它从背景中凸显出来:
css
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.10);根据主题调整不透明度:浅色背景用更淡的阴影,深色背景用更深的阴影。
侧边按钮(可选细节,提升真实感):
- 电源键:右侧,距离顶部38%位置,高度为总高度的8%,宽度为宽度的0.6%
- 音量加减键:左侧,堆叠在距离顶部32%和42%位置,尺寸和电源键相同
Premium Design Elements
高级设计元素
The difference between a basic screenshot and a premium-looking one is entirely in the decorative layer — background treatment, label design, and subtle depth behind the phone. All of these are pure CSS applied to the slide canvas.
基础截图和高级截图的区别完全在于装饰层:背景处理、标签设计和手机背后的微妙层次感。所有这些都是应用到截图画布的纯CSS效果。
Background treatments
背景处理
Never use a flat solid fill for the slide background. Layer at least two elements:
- Base gradient — diagonal or radial, multi-stop, using brand colors
- Decorative orbs — 2–3 large, heavily blurred circles placed behind the text and phone
css
/* Example: dark theme with purple brand */
.slide {
background: linear-gradient(160deg, #080613 0%, #120B22 60%, #060410 100%);
position: relative;
overflow: hidden;
}
/* Blurred decorative orb */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(140px);
pointer-events: none;
z-index: 1;
}
/* Top orb — sits behind the text block */
.orb-top {
width: 700px; height: 700px;
top: -120px; left: 50%;
transform: translateX(-50%);
background: radial-gradient(circle, #7C3AED 0%, transparent 70%);
opacity: 0.28;
}
/* Bottom orb — sits behind the phone */
.orb-bottom {
width: 900px; height: 900px;
top: 500px; left: 50%;
transform: translateX(-50%);
background: radial-gradient(circle, #4338CA 0%, transparent 70%);
opacity: 0.20;
}Adjust orb opacity by theme:
- Dark themes:
0.25–0.40 - Light themes: (very soft tinted wash, barely visible)
0.08–0.15
永远不要用平涂纯色作为截图背景,至少叠加两层元素:
- 基础渐变 — 对角或径向,多色阶,使用品牌色
- 装饰光晕 — 2-3个大尺寸、高度模糊的圆形,放在文本和手机背后
css
/* 示例:紫色品牌的深色主题 */
.slide {
background: linear-gradient(160deg, #080613 0%, #120B22 60%, #060410 100%);
position: relative;
overflow: hidden;
}
/* 模糊装饰光晕 */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(140px);
pointer-events: none;
z-index: 1;
}
/* 顶部光晕 — 位于文本块背后 */
.orb-top {
width: 700px; height: 700px;
top: -120px; left: 50%;
transform: translateX(-50%);
background: radial-gradient(circle, #7C3AED 0%, transparent 70%);
opacity: 0.28;
}
/* 底部光晕 — 位于手机背后 */
.orb-bottom {
width: 900px; height: 900px;
top: 500px; left: 50%;
transform: translateX(-50%);
background: radial-gradient(circle, #4338CA 0%, transparent 70%);
opacity: 0.20;
}根据主题调整光晕不透明度:
- 深色主题:
0.25–0.40 - 浅色主题:(非常柔和的着色,几乎不可见)
0.08–0.15
Label as pill badge
药丸形标签
The label must be a pill-shaped badge with a background — not plain text. This single change makes slides look designed rather than assembled.
css
/* All values below are illustrative — scale relative to W as shown in comments */
.label-pill {
display: inline-flex;
align-items: center;
padding: 12px 32px; /* W*0.011 W*0.030 */
border-radius: 100px;
font-size: 28px; /* W * 0.026 */
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
margin-bottom: 32px; /* W * 0.030 */
}
/* Dark theme — glass/translucent variant */
.label-pill-glass {
background: rgba(255, 255, 255, 0.10);
color: rgba(255, 255, 255, 0.80);
border: 1.5px solid rgba(255, 255, 255, 0.14);
}
/* Dark theme — colored variant (hero slide or high-emphasis labels) */
.label-pill-accent {
background: rgba(124, 58, 237, 0.18); /* use brand primary at ~18% */
color: #C4B5FD; /* lighter tint of primary */
border: 1.5px solid rgba(124, 58, 237, 0.32);
}
/* Light theme */
.label-pill-light {
background: rgba(201, 130, 42, 0.12); /* brand primary at ~12% */
color: #92600E; /* darker shade for contrast */
border: 1.5px solid rgba(201, 130, 42, 0.25);
}标签必须是带背景的药丸形徽章,而不是纯文本。这个改动就能让截图页看起来是专门设计的,而不是简单拼接的。
css
/* 以下数值仅作示例 — 按注释说明相对于宽度W缩放 */
.label-pill {
display: inline-flex;
align-items: center;
padding: 12px 32px; /* W*0.011 W*0.030 */
border-radius: 100px;
font-size: 28px; /* W * 0.026 */
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
margin-bottom: 32px; /* W * 0.030 */
}
/* 深色主题 — 玻璃/半透明变体 */
.label-pill-glass {
background: rgba(255, 255, 255, 0.10);
color: rgba(255, 255, 255, 0.80);
border: 1.5px solid rgba(255, 255, 255, 0.14);
}
/* 深色主题 — 彩色变体(首屏或高强调标签) */
.label-pill-accent {
background: rgba(124, 58, 237, 0.18); /* 主品牌色透明度约18% */
color: #C4B5FD; /* 主色的浅色调 */
border: 1.5px solid rgba(124, 58, 237, 0.32);
}
/* 浅色主题 */
.label-pill-light {
background: rgba(201, 130, 42, 0.12); /* 主品牌色透明度约12% */
color: #92600E; /* 更深的阴影保证对比度 */
border: 1.5px solid rgba(201, 130, 42, 0.25);
}Headline typography
标题排版
Apply tight letter-spacing and a subtle text shadow for depth:
css
/* font-size must be set via inline style using the proportional system — not hardcoded */
.headline {
/* font-size: W * 0.090 (set inline, not in CSS class) */
font-weight: 800;
line-height: 1.05;
letter-spacing: -0.02em;
}
/* Dark theme only */
.slide-dark .headline {
text-shadow: 0 4px 40px rgba(0, 0, 0, 0.40);
}应用紧凑的字间距和微妙的文字阴影增加层次感:
css
/* 字号必须通过内联样式使用比例系统设置 — 不要硬编码 */
.headline {
/* font-size: W * 0.090 (内联设置,不要写在CSS类中) */
font-weight: 800;
line-height: 1.05;
letter-spacing: -0.02em;
}
/* 仅深色主题 */
.slide-dark .headline {
text-shadow: 0 4px 40px rgba(0, 0, 0, 0.40);
}App icon in header
头部的应用图标
Show the app icon above the label (≈80px, rounded). This anchors brand identity on every slide without adding clutter.
css
.app-icon {
width: 80px; height: 80px;
border-radius: 18px;
object-fit: cover;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.30);
margin-bottom: 20px;
}在标签上方展示应用图标(约80px,圆角)。这能在每个截图页锚定品牌标识,同时不会增加杂乱感。
css
.app-icon {
width: 80px; height: 80px;
border-radius: 18px;
object-fit: cover;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.30);
margin-bottom: 20px;
}Phone ambient glow (dark themes only)
手机环境光晕(仅深色主题)
A radial glow placed between the background and the phone makes the device feel embedded in light rather than pasted on top:
css
.phone-glow {
position: absolute;
width: 900px; height: 900px;
border-radius: 50%;
background: radial-gradient(circle, var(--brand-primary) 0%, transparent 65%);
filter: blur(80px);
opacity: 0.18;
top: 400px;
left: 50%;
transform: translateX(-50%);
pointer-events: none;
z-index: 4; /* above background orbs, below phone */
}Do not add glow on light backgrounds — it reads as a muddy stain.
放在背景和手机之间的径向光晕,让设备感觉是嵌入在光线中,而不是粘贴在背景上:
css
.phone-glow {
position: absolute;
width: 900px; height: 900px;
border-radius: 50%;
background: radial-gradient(circle, var(--brand-primary) 0%, transparent 65%);
filter: blur(80px);
opacity: 0.18;
top: 400px;
left: 50%;
transform: translateX(-50%);
pointer-events: none;
z-index: 4; /* 位于背景光晕上方,手机下方 */
}不要在浅色背景上添加光晕,会看起来像脏污的痕迹。
Noise texture overlay (optional, high-end finish)
噪点纹理叠加(可选,高级质感)
A subtle SVG noise texture makes gradients feel less flat and more printed. Keep opacity very low:
css
.slide::after {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
opacity: 0.04;
mix-blend-mode: overlay;
pointer-events: none;
z-index: 100;
}微妙的SVG噪点纹理让渐变看起来不那么扁平,更有印刷质感。保持非常低的不透明度:
css
.slide::after {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
opacity: 0.04;
mix-blend-mode: overlay;
pointer-events: none;
z-index: 100;
}Export Specifications
导出规范
Phone Portrait (primary — always required)
手机竖版(核心 — 必须提供)
1080 × 1920px
Design and export everything at this resolution. Play Store accepts any size with an aspect ratio between 1:2 and 2:1 — 1080×1920 covers all phones.
1080 × 1920px
所有设计和导出都使用这个分辨率。Play Store接受宽高比在1:2到2:1之间的任何尺寸,1080×1920覆盖所有手机。
Phone Landscape (optional)
手机横版(可选)
1920 × 1080px
Useful for games and apps with landscape-first UIs. Offer this when the user's app is primarily used in landscape orientation.
1920 × 1080px
适合游戏和优先横版UI的应用。当用户的应用主要在横屏方向使用时提供这个选项。
Tablet (optional)
平板(可选)
| Screen | Dimensions |
|---|---|
| 7-inch | 1200 × 1920px |
| 10-inch | 1600 × 2560px |
| 屏幕 | 尺寸 |
|---|---|
| 7英寸 | 1200 × 1920px |
| 10英寸 | 1600 × 2560px |
Feature Graphic (required by Play Store)
特色图(Play Store要求必须提供)
1024 × 500px
This is a landscape banner shown at the top of your Play Store listing. It is not a phone screenshot — it has no device frame. Required for all apps.
1024 × 500px
这是展示在Play Store详情页顶部的横版横幅,不是手机截图 — 没有设备边框。所有应用都必须提供。
Constants
常量
typescript
// Phone portrait (primary)
const PHONE_W = 1080;
const PHONE_H = 1920;
// Phone landscape (optional)
const PHONE_LAND_W = 1920;
const PHONE_LAND_H = 1080;
// Tablet 7-inch (optional)
const TAB7_W = 1200;
const TAB7_H = 1920;
// Tablet 10-inch (optional)
const TAB10_W = 1600;
const TAB10_H = 2560;
// Feature graphic (required)
const FG_W = 1024;
const FG_H = 500;typescript
// 手机竖版(核心)
const PHONE_W = 1080;
const PHONE_H = 1920;
// 手机横版(可选)
const PHONE_LAND_W = 1920;
const PHONE_LAND_H = 1080;
// 7英寸平板(可选)
const TAB7_W = 1200;
const TAB7_H = 1920;
// 10英寸平板(可选)
const TAB10_W = 1600;
const TAB10_H = 2560;
// 特色图(必须)
const FG_W = 1024;
const FG_H = 500;Copy Strategy & Messaging
文案策略与信息传递
Each slide has three text elements: a label, a headline, and an optional subtext. Each has a different job. Get all three right and the slide sells itself in one second.
每个截图页有三个文本元素:标签、标题和可选的子文案。每个元素有不同的作用,三者都做好的话,截图页一秒就能传达卖点。
Step 1: Go from feature description to copy
步骤1:从功能描述转化为文案
When the user describes a feature, do not describe it back to them. Ask instead:
"What would make someone anxious or frustrated if this feature didn't exist?"
That anxiety is your headline. The feature is just the solution to it.
Example:
- User says: "Offline mode — the app works without internet"
- Anxiety: Being somewhere with no signal when you need the app
- Headline: or
"No signal?\nNo problem.""Every prayer.\nAlways there." - NOT: or
"Works offline""Offline support"
Example:
- User says: "You can change the font size"
- Anxiety: Straining to read small text, especially for older users
- Headline: or
"Bigger text.\nClearer words.""Read your way." - NOT: or
"Adjustable font size""Accessibility features"
Example:
- User says: "Save favourites / bookmark prayers"
- Anxiety: Scrolling through everything to find the one you use most
- Headline: or
"Your prayers.\nAlways close.""Save what\nyou love." - NOT: or
"Favourites system""Bookmark your content"
当用户描述一个功能时,不要直接把功能描述复述回去,而是要问:
"如果没有这个功能,用户会感到什么焦虑或困扰?"
那个焦虑就是你的标题,功能只是解决焦虑的方案。
示例:
- 用户说:"离线模式 — 应用没有网络也能使用"
- 焦虑:需要用应用的时候没有信号
- 标题:或
"没信号?\n没问题。""所有祷告\n随时可用。" - 错误写法:或
"支持离线使用""离线模式"
示例:
- 用户说:"可以调整字体大小"
- 焦虑:看不清小字,尤其是老年用户
- 标题:或
"字体更大\n阅读更清""按你的习惯阅读" - 错误写法:或
"可调整字体大小""无障碍功能"
示例:
- 用户说:"收藏/书签祷告内容"
- 焦虑:要翻找很久才能找到常用的内容
- 标题:或
"你的祷告\n触手可及""收藏你喜欢的内容" - 错误写法:或
"收藏系统""书签功能"
Step 2: Write the headline
步骤2:编写标题
The headline is the largest text on the slide. It must land in under one second at thumbnail size.
Six formulas that work. Pick the one that fits the feature's emotional angle:
标题是截图页上最大的文本,在缩略图大小下必须一秒就能看懂。
六个经过验证的公式,选择最符合功能情感角度的那个:
Formula 1: Solve the pain
公式1:解决痛点
State the problem and imply the solution. Short, punchy.
"No signal?\nNo problem.""Stop searching.\nJust find it.""Forgot again?\nNever again."
陈述问题,暗示解决方案,简短有力。
"没信号?\n没问题。""不用搜索\n即刻找到。""又忘了?\n再也不会。"
Formula 2: Own it (possessive + outcome)
公式2:归属感(所有格+结果)
Creates a sense of personal ownership. Feels intimate.
"Your prayers.\nAlways close.""Your pace.\nYour progress.""Your data.\nYour call."
创造个人拥有感,感觉更亲密。
"你的祷告\n随时都在。""你的节奏\n你的进度。""你的数据\n你说了算。"
Formula 3: The moment (paint the exact instant)
公式3:场景化(描绘具体使用瞬间)
Describes the specific moment the user benefits. Makes it tangible.
"Every prayer.\nAt your fingertips.""Done by noon.\nEvery day.""Open the app.\nStart the calm."
描述用户受益的具体时刻,更具象。
"每次祷告\n触手可及。""中午之前\n全部完成。""打开应用\n即刻平静。"
Formula 4: Simple contrast (before vs after, implied)
公式4:简单对比(隐含前后对比)
Two short lines. First creates tension, second resolves it.
"Bigger text.\nClearer words.""One tap.\nInstant access.""Less noise.\nMore focus."
两行短句,第一句制造张力,第二句解决。
"字体更大\n文字更清。""一键点击\n即刻访问。""更少干扰\n更多专注。"
Formula 5: The invitation (active verb + reward)
公式5:邀请式(主动动词+奖励)
Starts with a verb. Energetic, direct.
"Save what\nyou love.""Find anything.\nInstantly.""Switch languages.\nAnytime."
以动词开头,有活力,直接。
"收藏你\n热爱的内容。""找任何内容\n即刻找到。""切换语言\n随时随地。"
Formula 6: The declaration (confident statement of identity)
公式6:宣言式(自信的身份声明)
What the app IS, stated boldly. Best for hero slide.
"Every prayer.\nAt your fingertips.""Focus like\nyou mean it.""Your daily\ndevotion. Simplified."
大胆陈述应用是什么,最适合首屏。
"所有祷告\n触手可及。""专注到\n你满意。""你的日常灵修\n简化之选。"
Step 3: Write the label
步骤3:编写标签
The label is the small all-caps tag above the headline. Its job is to prime the reader — tell them what category of benefit is coming so the headline lands harder.
Rules:
- 1–3 words, all caps
- Name the benefit category, not the feature name
- Should make the reader curious about the headline below it
| Feature | Bad label | Good label |
|---|---|---|
| Offline mode | | |
| Font size setting | | |
| Favourites | | |
| Bilingual | | |
| Dark mode | | |
| Daily reminder | | |
标签是标题上方的全大写小标签,作用是引导读者 — 告诉他们接下来的收益类别,让标题更有冲击力。
规则:
- 1-3个单词,全大写
- 命名收益类别,而不是功能名称
- 要让读者对下面的标题产生好奇
| 功能 | 不好的标签 | 好的标签 |
|---|---|---|
| 离线模式 | | |
| 字体大小设置 | | |
| 收藏 | | |
| 双语 | | |
| 深色模式 | | |
| 每日提醒 | | |
Step 4: Write the subtext
步骤4:编写子文案
The subtext is one supporting sentence below the headline. It is optional — leave it empty when the headline is strong enough alone (especially on the hero slide).
When you do use it:
- Add ONE specific detail the headline deliberately left vague
- Make it conversational, not corporate
- Max 8–10 words
- Do not restate the headline in different words
| Headline | Bad subtext | Good subtext |
|---|---|---|
| "Access all your prayers easily in one place" | "Aarti • Chalisa • Mantra — all in one place" |
| "The app works without an internet connection" | "Every prayer, always available." |
| "You can adjust the font size for better readability" | "Adjust font size to suit you." |
| "Save your favourite prayers as bookmarks" | "Quick access to your most-used prayers." |
子文案是标题下方的一句补充说明,可选 — 如果标题本身足够有力就留空(尤其是首屏)。
使用子文案时:
- 补充一个标题故意没说的具体细节
- 口语化,不要官方话术
- 最多8-10个单词
- 不要用不同的话复述标题
| 标题 | 不好的子文案 | 好的子文案 |
|---|---|---|
| "在一个地方轻松访问所有祷告内容" | "Aarti • Chalisa • Mantra 全都有" |
| "应用没有网络连接也能使用" | "所有祷告随时可用" |
| "你可以调整字体大小提升可读性" | "可自定义适合你的字体大小" |
| "把你喜欢的祷告保存为书签" | "快速访问你最常用的祷告" |
Rules that apply to everything
通用规则
One idea per slide. If you find yourself writing "and", you have two slides, not one.
3–5 words per line. Break lines intentionally — the line break is part of the design, not an accident of wrapping.
Short words win. 1–2 syllables. Test: would a 10-year-old understand this instantly?
Verbs over nouns. "Save what you love" beats "Favourites management". "Find anything" beats "Search functionality".
Never use these words: seamless, powerful, advanced, robust, intuitive, innovative, comprehensive, streamlined, leverage, utilize. They say nothing.
每页一个想法。 如果你写了"和",说明你需要两页,而不是一页。
每行3-5个单词。 故意换行 — 换行是设计的一部分,不是自动换行的意外结果。
短单词更好。 1-2个音节,测试:10岁小孩能不能立刻看懂?
动词优于名词。 "收藏你喜欢的内容"比"收藏管理"好,"找任何内容"比"搜索功能"好。
永远不要用这些词: 无缝的、强大的、先进的、健壮的、直观的、创新的、全面的、精简的、利用、使用。这些词没有实质意义。
Complete bad → better reference
完整的坏→好参考
| Feature description | Bad headline | Good headline |
|---|---|---|
| "Task management with priorities" | "Advanced task management" | |
| "Syncs across devices" | "Cross-device sync" | |
| "Analytics dashboard" | "Powerful analytics" | |
| "Push notifications" | "Real-time notifications" | |
| "Home screen widget" | "Customizable widgets" | |
| "Offline mode" | "Works offline" | |
| "Font size control" | "Accessibility features" | |
| "Save favourites" | "Bookmark system" | |
| "Dark mode" | "Dark mode support" | |
| "Language switch" | "Bilingual support" | |
| 功能描述 | 不好的标题 | 好的标题 |
|---|---|---|
| "带优先级的任务管理" | "高级任务管理" | |
| "多设备同步" | "跨设备同步" | |
| "分析看板" | "强大的分析功能" | |
| "推送通知" | "实时通知" | |
| "桌面组件" | "可自定义组件" | |
| "离线模式" | "离线可用" | |
| "字体大小控制" | "无障碍功能" | |
| "收藏功能" | "书签系统" | |
| "深色模式" | "支持深色模式" | |
| "语言切换" | "双语支持" | |
Feature graphic headline
特色图标题
Same rules, slightly longer — up to 8 words since the landscape canvas has more room. Should capture the app's core promise in a single line. No line breaks needed.
✓"The app that changes how you pray"✓"Every devotional. One beautiful app."✓"Your daily devotional companion"
规则相同,稍微长一点 — 最多8个单词,因为横版画布空间更大。要用一句话抓住应用的核心承诺,不需要换行。
✓"改变你祷告方式的应用"✓"所有灵修内容 一个精美应用"✓"你的日常灵修伴侣"
Technical Implementation
技术实现
Setup
初始化
Create a new folder and two files inside it:
bash
mkdir play-store-screenshots
cd play-store-screenshots
mkdir screenshots创建新文件夹,里面新建两个文件:
bash
mkdir play-store-screenshots
cd play-store-screenshots
mkdir screenshotsCopy the user's app screenshots and icon into ./screenshots/
把用户的应用截图和图标复制到 ./screenshots/ 目录下
Then create `config.js` (AI fills this in) and `index.html` (AI generates this). That's the entire setup — no package manager, no install step.
然后创建`config.js`(AI填充内容)和`index.html`(AI生成)。整个设置就完成了 — 无需包管理器,无需安装步骤。index.html structure
index.html结构
The loads everything via CDN — no local dependencies:
<head>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- Google Fonts — match user's preference, default to Inter -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet">
<!-- html-to-image for PNG export -->
<script src="https://unpkg.com/html-to-image@1.11.11/dist/html-to-image.js"></script>
<!-- JSZip + FileSaver — bundle all exports into a single ZIP download -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<!-- User's config — must come before the main script -->
<script src="config.js"></script>
</head>
<body>
<!-- Top bar: app name, locale selector, Export ZIP button -->
<div id="topbar">...</div>
<!-- Slide grid: ALL slides visible at once, each scaled to fit viewport height.
This is the default and only view — no toggle needed. -->
<div id="slide-grid">
<!-- Phone slides (one .slide-wrap per slide) -->
<!-- Feature graphic last, same row, scaled proportionally -->
</div>
<!-- Off-screen export canvases: always full resolution, never scaled -->
<div id="export-canvases" style="position:fixed;left:-9999px;top:0;z-index:-1">...</div>
<script>
// All rendering and export logic here
</script>
</body>
</html>Key principle: all slides are always visible in the grid. There is no "one at a time" mode and no toggle. The grid IS the interface — it lets you QA all slides at a glance before exporting.
<head>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- Google Fonts — 匹配用户偏好,默认Inter -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet">
<!-- html-to-image 用于PNG导出 -->
<script src="https://unpkg.com/html-to-image@1.11.11/dist/html-to-image.js"></script>
<!-- JSZip + FileSaver — 把所有导出文件打包成单个ZIP下载 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<!-- 用户配置 — 必须在主脚本之前加载 -->
<script src="config.js"></script>
</head>
<body>
<!-- 顶部栏:应用名称、语言选择器、导出ZIP按钮 -->
<div id="topbar">...</div>
<!-- 截图网格:所有截图同时可见,每个都缩放适配视口高度。
这是默认且唯一的视图 — 不需要切换按钮。 -->
<div id="slide-grid">
<!-- 手机截图页(每页一个.slide-wrap) -->
<!-- 特色图在最后,同一行,按比例缩放 -->
</div>
<!-- 屏外导出画布:始终全分辨率,永不缩放 -->
<div id="export-canvases" style="position:fixed;left:-9999px;top:0;z-index:-1">...</div>
<script>
// 所有渲染和导出逻辑写在这里
</script>
</body>
</html>核心原则:所有截图始终在网格中可见。没有"一次看一个"的模式,也没有切换按钮。 网格就是界面 — 让你在导出前一眼就能检查所有截图。
Typography
排版
Load the font via the tag above. Good defaults: Inter, Plus Jakarta Sans, DM Sans. Match the user's preference if they specified one.
<link>Scale all font sizes relative to canvas width — never hardcode values in slide rendering:
pxjavascript
// Typography scale — derive from canvas width W
function typography(W) {
return {
label: W * 0.028, // all-caps category label
headline: W * 0.090, // standard headline (1–2 lines)
headlineLg: W * 0.100, // hero slide headline (larger)
subtext: W * 0.038, // supporting sentence
caption: W * 0.030, // small secondary text
};
}This ensures text renders correctly at all export resolutions without changes.
通过上面的标签加载字体,好的默认选择:Inter、Plus Jakarta Sans、DM Sans。如果用户指定了字体就匹配用户偏好。
<link>所有字号相对画布宽度缩放 — 永远不要在截图渲染中硬编码值:
pxjavascript
// 排版比例 — 从画布宽度W推导
function typography(W) {
return {
label: W * 0.028, // 全大写类别标签
headline: W * 0.090, // 标准标题(1-2行)
headlineLg: W * 0.100, // 首屏标题(更大)
subtext: W * 0.038, // 补充说明
caption: W * 0.030, // 小号次要文本
};
}这保证文本在所有导出分辨率下都能正确渲染,无需修改。
Rendering headline newlines
渲染标题换行
Critical: Headlines in use to break lines (e.g. ). Never rely on to render these — that only works with actual newline characters, not the literal two-character sequence that config values may contain. Always convert using a helper before inserting into innerHTML:
config.js\n"Every prayer.\nAt your fingertips."white-space: pre-line\njavascript
const nl2br = (text) => text.replace(/\\n|\n/g, '<br>');Use and everywhere you insert these values into HTML.
nl2br(slide.headline)nl2br(slide.subtext)重要: 中的标题使用换行(例如)。永远不要依赖来渲染换行 — 它只对真实的换行字符有效,对配置值中可能包含的字面量两个字符无效。插入到innerHTML前始终用辅助函数转换:
config.js\n"所有祷告.\n触手可及."white-space: pre-line\njavascript
const nl2br = (text) => text.replace(/\\n|\n/g, '<br>');所有插入HTML的地方都使用和。
nl2br(slide.headline)nl2br(slide.subtext)Theme System
主题系统
Four named themes as plain JS objects. The key selects between them:
config.themejavascript
const THEMES = {
"clean-light": {
bg: "#FFFFFF",
fg: "#111111",
accent: config.brand.primary,
muted: "#888888",
surface: "#F4F4F8",
},
"dark-bold": {
bg: "#0A0A0A",
fg: "#FFFFFF",
accent: config.brand.accent,
muted: "#999999",
surface: "#1A1A1A",
},
"vibrant": {
bg: config.brand.primary,
fg: "#FFFFFF",
accent: config.brand.accent,
muted: "rgba(255,255,255,0.7)",
surface: config.brand.secondary,
},
"editorial": {
bg: "#F8F5F0",
fg: "#1A1A1A",
accent: config.brand.primary,
muted: "#7A7A7A",
surface: "#EDE9E3",
},
};
const theme = THEMES[config.theme];四个命名主题作为纯JS对象,通过键选择:
config.themejavascript
const THEMES = {
"clean-light": {
bg: "#FFFFFF",
fg: "#111111",
accent: config.brand.primary,
muted: "#888888",
surface: "#F4F4F8",
},
"dark-bold": {
bg: "#0A0A0A",
fg: "#FFFFFF",
accent: config.brand.accent,
muted: "#999999",
surface: "#1A1A1A",
},
"vibrant": {
bg: config.brand.primary,
fg: "#FFFFFF",
accent: config.brand.accent,
muted: "rgba(255,255,255,0.7)",
surface: config.brand.secondary,
},
"editorial": {
bg: "#F8F5F0",
fg: "#1A1A1A",
accent: config.brand.primary,
muted: "#7A7A7A",
surface: "#EDE9E3",
},
};
const theme = THEMES[config.theme];Slide grid scaling
截图网格缩放
Scale each phone slide so it fits comfortably within the viewport height. Compute a single scale factor and apply it to all slide wrappers:
javascript
const TOPBAR_H = 52; // height of the top controls bar in px
const SCALE = Math.min(
(window.innerHeight - TOPBAR_H - 64) / PHONE_H, // fit vertically
(window.innerWidth - 80) / (PHONE_W * config.slides.length + 20 * (config.slides.length - 1))
);
document.querySelectorAll(".slide-wrap").forEach(wrap => {
wrap.style.width = `${PHONE_W * SCALE}px`;
wrap.style.height = `${PHONE_H * SCALE}px`;
});
document.querySelectorAll(".slide").forEach(slide => {
slide.style.transform = `scale(${SCALE})`;
slide.style.transformOrigin = "top left";
});The feature graphic renders on the same row. Scale it proportionally so its height visually matches the phone slides:
javascript
const fgScale = (PHONE_H * SCALE) / FG_H;
fgWrap.style.width = `${FG_W * fgScale}px`;
fgWrap.style.height = `${FG_H * fgScale}px`;
fgEl.style.transform = `scale(${fgScale})`;
fgEl.style.transformOrigin = "top left";The off-screen export canvases are always at full resolution — never scale them.
缩放每个手机截图页,让它舒适适配视口高度。计算单个缩放因子,应用到所有截图包装器:
javascript
const TOPBAR_H = 52; // 顶部控制栏高度,单位px
const SCALE = Math.min(
(window.innerHeight - TOPBAR_H - 64) / PHONE_H, // 垂直适配
(window.innerWidth - 80) / (PHONE_W * config.slides.length + 20 * (config.slides.length - 1))
);
document.querySelectorAll(".slide-wrap").forEach(wrap => {
wrap.style.width = `${PHONE_W * SCALE}px`;
wrap.style.height = `${PHONE_H * SCALE}px`;
});
document.querySelectorAll(".slide").forEach(slide => {
slide.style.transform = `scale(${SCALE})`;
slide.style.transformOrigin = "top left";
});特色图渲染在同一行,按比例缩放让它的高度和手机截图视觉上匹配:
javascript
const fgScale = (PHONE_H * SCALE) / FG_H;
fgWrap.style.width = `${FG_W * fgScale}px`;
fgWrap.style.height = `${FG_H * fgScale}px`;
fgEl.style.transform = `scale(${fgScale})`;
fgEl.style.transformOrigin = "top left";屏外导出画布始终保持全分辨率 — 永远不要缩放它们。
Feature Graphic Implementation
特色图实现
The feature graphic is a standalone component, not a phone slide. It renders on its own 1024×500px canvas.
特色图是独立组件,不是手机截图页,在自己的1024×500px画布上渲染。
Critical: enforce exact canvas dimensions
重要:强制精确画布尺寸
The canvas element must be explicitly sized in CSS — never let the browser size it from content:
css
#feature-graphic-canvas {
width: 1024px;
height: 500px;
position: relative;
overflow: hidden;
/* Never set width/height from JS or let it auto-size */
}In the grid preview, apply a CSS to resize it visually, but keep the underlying DOM element at exactly 1024×500. If the canvas is sized any other way, the exported PNG will have wrong dimensions.
transform: scale(...)画布元素必须在CSS中显式设置尺寸 — 永远不要让浏览器根据内容自动调整大小:
css
#feature-graphic-canvas {
width: 1024px;
height: 500px;
position: relative;
overflow: hidden;
/* 永远不要通过JS设置宽高,也不要让它自动大小 */
}在网格预览中,应用CSS 来视觉上调整大小,但底层DOM元素始终保持精确的1024×500。如果画布用其他方式调整大小,导出的PNG尺寸就会出错。
transform: scale(...)Three styles (set via featureGraphic.style
in config)
featureGraphic.style三种样式(通过config中的featureGraphic.style
设置)
featureGraphic.stylegradient
gradientgradient
gradientFull canvas diagonal gradient from to . Overlay 2–3 blurred orbs for depth (same technique as phone slides). App icon on the left third (centered vertically, 180×180px). Headline right of center, subtext below.
brand.primarybrand.secondary┌──────────────────────────────────────────────────────────┐
│ ░░░░░░░░░░░░░░░░ gradient bg + subtle orbs ░░░░░░░░░ │
│ │
│ ┌──────┐ App Name │
│ │ ICON │ │
│ │ │ Big headline here │
│ └──────┘ Available on Google Play │
│ │
└──────────────────────────────────────────────────────────┘Best for: most apps. Clean, professional, works with any brand color.
整个画布的对角渐变从过渡到。叠加2-3个模糊光晕增加层次感(和手机截图页的技术相同)。应用图标在左侧三分之一区域(垂直居中,180×180px)。标题在中心偏右,子文案在标题下方。
brand.primarybrand.secondary┌──────────────────────────────────────────────────────────┐
│ ░░░░░░░░░░░░░░░░ 渐变背景 + 微妙光晕 ░░░░░░░░░ │
│ │
│ ┌──────┐ 应用名称 │
│ │ 图标 │ │
│ │ │ 大标题在这里 │
│ └──────┘ 可在Google Play下载 │
│ │
└──────────────────────────────────────────────────────────┘最适合:大多数应用,干净专业,适配任何品牌色。
solid
solidsolid
solidFlat fill with a thin horizontal accent line () and large centered app icon. Headline and subtext centered below.
brand.backgroundbrand.primaryBest for: utility apps, productivity tools, apps with a clean/minimal brand identity.
平涂填充,搭配细的水平强调线()和大的居中应用图标。标题和子文案在图标下方居中。
最适合:工具类应用、生产力工具、干净/极简品牌标识的应用。
brand.backgroundbrand.primaryscreenshot-backed
screenshot-backedscreenshot-backed
screenshot-backedApp screenshot fills the right 55% of the canvas (cropped, object-fit cover). Left 45% is a solid panel. Feathered gradient fades between the two halves. Headline and icon sit on the solid panel.
brand.primaryBest for: visually rich apps (photography, design, social) where showing the UI is itself a selling point.
应用截图填充画布右侧55%区域(裁剪,object-fit cover)。左侧45%是纯色面板。羽化渐变在两个半区之间过渡。标题和图标放在纯色面板上。
最适合:视觉丰富的应用(摄影、设计、社交),展示UI本身就是卖点的场景。
brand.primaryPremium feature graphic composition
高级特色图构图
Apply the same decorative orbs technique to the feature graphic:
css
.fg-orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
/* Large orb upper-left, behind icon */
.fg-orb-1 {
width: 300px; height: 300px;
top: -60px; left: -40px;
background: radial-gradient(circle, var(--brand-secondary) 0%, transparent 70%);
opacity: 0.40;
}
/* Smaller orb lower-right */
.fg-orb-2 {
width: 200px; height: 200px;
bottom: -40px; right: 80px;
background: radial-gradient(circle, var(--brand-primary) 0%, transparent 70%);
opacity: 0.30;
}对特色图应用同样的装饰光晕技术:
css
.fg-orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
/* 左上角大光晕,位于图标背后 */
.fg-orb-1 {
width: 300px; height: 300px;
top: -60px; left: -40px;
background: radial-gradient(circle, var(--brand-secondary) 0%, transparent 70%);
opacity: 0.40;
}
/* 右下角小光晕 */
.fg-orb-2 {
width: 200px; height: 200px;
bottom: -40px; right: 80px;
background: radial-gradient(circle, var(--brand-primary) 0%, transparent 70%);
opacity: 0.30;
}Typography for feature graphic (scale from FG_W = 1024
)
FG_W = 1024特色图排版(从FG_W = 1024
缩放)
FG_W = 1024javascript
const FGT = {
headline: FG_W * 0.060, // ~61px — confident, reads at small sizes
subtext: FG_W * 0.026, // ~27px
appName: FG_W * 0.030, // ~31px — shown above the headline
};javascript
const FGT = {
headline: FG_W * 0.060, // ~61px — 醒目,小尺寸下也能看清
subtext: FG_W * 0.026, // ~27px
appName: FG_W * 0.030, // ~31px — 展示在标题上方
};Export Mechanism
导出机制
Use (preferred over for superior CSS gradient, filter, and support). Use JSZip + FileSaver.js to bundle all exports into a single ZIP download.
html-to-imagehtml2canvasclip-path使用(比更优,对CSS渐变、滤镜和的支持更好)。使用JSZip + FileSaver.js把所有导出文件打包成单个ZIP下载。
html-to-imagehtml2canvasclip-pathCritical double-call pattern
重要的两次调用模式
This is required for correct font and image rendering. The first call warms up lazy-loaded assets; the second produces clean output. Skipping the first call produces blurry text or missing images.
The helper below implements this pattern and is used by :
captureSlideexportAlljavascript
// Moves the element on-screen momentarily (required by html-to-image),
// captures twice for clean output, then returns the data URL.
async function captureSlide(element) {
element.style.left = "0px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "9999";
// First call: warms up fonts and images — result discarded
await htmlToImage.toPng(element, { pixelRatio: 1 });
// Second call: clean, correct output
const dataUrl = await htmlToImage.toPng(element, { pixelRatio: 1 });
// Restore to off-screen position
element.style.left = "-9999px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "-1";
await new Promise((r) => setTimeout(r, 200));
return dataUrl;
}这是保证字体和图片正确渲染的必要步骤。第一次调用预热懒加载资源,第二次调用生成清晰的输出。跳过第一次调用会导致文字模糊或图片缺失。
下面的辅助函数实现了这个模式,供使用:
captureSlideexportAlljavascript
// 把元素临时移动到屏幕上(html-to-image要求),
// 捕获两次获得清晰输出,然后返回data URL。
async function captureSlide(element) {
element.style.left = "0px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "9999";
// 第一次调用:预热字体和图片 — 丢弃结果
await htmlToImage.toPng(element, { pixelRatio: 1 });
// 第二次调用:清晰、正确的输出
const dataUrl = await htmlToImage.toPng(element, { pixelRatio: 1 });
// 恢复到屏外位置
element.style.left = "-9999px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "-1";
await new Promise((r) => setTimeout(r, 200));
return dataUrl;
}Filename convention
文件名规范
Zero-padded numeric prefix ensures correct alphabetical sort order in the file system and when uploading to Play Store:
exports/phone/en/
├── 01-hero-1080x1920.png
├── 02-feature-one-1080x1920.png
├── 03-feature-two-1080x1920.png
├── 04-trust-1080x1920.png
└── 05-cta-1080x1920.png
exports/feature-graphic/
└── feature-graphic-1024x500.png前补零的数字前缀保证文件系统和上传到Play Store时的字母排序正确:
exports/phone/en/
├── 01-hero-1080x1920.png
├── 02-feature-one-1080x1920.png
├── 03-feature-two-1080x1920.png
├── 04-trust-1080x1920.png
└── 05-cta-1080x1920.png
exports/feature-graphic/
└── feature-graphic-1024x500.pngBulk export as ZIP
批量导出为ZIP
All slides are collected into a single ZIP file and downloaded in one click. No individual file downloads.
javascript
// Capture helper — double-call pattern for clean font and image rendering
async function captureSlide(element) {
element.style.left = "0px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "9999";
// First call: warms up lazy-loaded fonts and images — result discarded
await htmlToImage.toPng(element, { pixelRatio: 1 });
// Second call: clean, correct output
const dataUrl = await htmlToImage.toPng(element, { pixelRatio: 1 });
element.style.left = "-9999px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "-1";
await new Promise((r) => setTimeout(r, 200));
return dataUrl;
}
async function exportAll() {
const zip = new JSZip();
const phoneFolder = zip.folder("phone");
const fgFolder = zip.folder("feature-graphic");
for (const locale of config.locales) {
const localeFolder = phoneFolder.folder(locale);
// Phone slides
for (let i = 0; i < config.slides.length; i++) {
const el = document.getElementById(`canvas-${locale}-${config.slides[i].id}`);
if (!el) continue;
const name = `${String(i + 1).padStart(2, "0")}-${config.slides[i].id}-${PHONE_W}x${PHONE_H}.png`;
const dataUrl = await captureSlide(el);
localeFolder.file(name, dataUrl.split(",")[1], { base64: true });
}
// Feature graphic
const fg = document.getElementById(`canvas-${locale}-feature-graphic`);
if (fg) {
const dataUrl = await captureSlide(fg);
fgFolder.file(`feature-graphic-${FG_W}x${FG_H}.png`, dataUrl.split(",")[1], { base64: true });
}
// Tablet slides (if enabled)
if (config.tablet && config.tablet.enabled) {
for (const size of config.tablet.sizes) {
const W = size === "10-inch" ? TAB10_W : TAB7_W;
const H = size === "10-inch" ? TAB10_H : TAB7_H;
const tabFolder = zip.folder(`tablet-${size}/${locale}`);
for (let i = 0; i < config.slides.length; i++) {
const el = document.getElementById(`canvas-tablet-${size}-${locale}-${config.slides[i].id}`);
if (!el) continue;
const name = `${String(i + 1).padStart(2, "0")}-${config.slides[i].id}-${W}x${H}.png`;
const dataUrl = await captureSlide(el);
tabFolder.file(name, dataUrl.split(",")[1], { base64: true });
}
}
}
}
// Generate and trigger download of the ZIP
const appSlug = config.app.name.replace(/\s+/g, "-").toLowerCase();
const blob = await zip.generateAsync({ type: "blob" });
saveAs(blob, `${appSlug}-play-store-screenshots.zip`);
}One click produces containing all slides organized by type and locale. Ready to unzip and upload to Play Console.
focusflow-play-store-screenshots.zip所有截图收集到单个ZIP文件中,一键下载,无需单独下载每个文件。
javascript
// 捕获辅助函数 — 两次调用模式保证字体和图片渲染清晰
async function captureSlide(element) {
element.style.left = "0px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "9999";
// 第一次调用:预热懒加载的字体和图片 — 丢弃结果
await htmlToImage.toPng(element, { pixelRatio: 1 });
// 第二次调用:清晰、正确的输出
const dataUrl = await htmlToImage.toPng(element, { pixelRatio: 1 });
element.style.left = "-9999px";
element.style.top = "0px";
element.style.position = "fixed";
element.style.zIndex = "-1";
await new Promise((r) => setTimeout(r, 200));
return dataUrl;
}
async function exportAll() {
const zip = new JSZip();
const phoneFolder = zip.folder("phone");
const fgFolder = zip.folder("feature-graphic");
for (const locale of config.locales) {
const localeFolder = phoneFolder.folder(locale);
// 手机截图页
for (let i = 0; i < config.slides.length; i++) {
const el = document.getElementById(`canvas-${locale}-${config.slides[i].id}`);
if (!el) continue;
const name = `${String(i + 1).padStart(2, "0")}-${config.slides[i].id}-${PHONE_W}x${PHONE_H}.png`;
const dataUrl = await captureSlide(el);
localeFolder.file(name, dataUrl.split(",")[1], { base64: true });
}
// 特色图
const fg = document.getElementById(`canvas-${locale}-feature-graphic`);
if (fg) {
const dataUrl = await captureSlide(fg);
fgFolder.file(`feature-graphic-${FG_W}x${FG_H}.png`, dataUrl.split(",")[1], { base64: true });
}
// 平板截图(如果启用)
if (config.tablet && config.tablet.enabled) {
for (const size of config.tablet.sizes) {
const W = size === "10-inch" ? TAB10_W : TAB7_W;
const H = size === "10-inch" ? TAB10_H : TAB7_H;
const tabFolder = zip.folder(`tablet-${size}/${locale}`);
for (let i = 0; i < config.slides.length; i++) {
const el = document.getElementById(`canvas-tablet-${size}-${locale}-${config.slides[i].id}`);
if (!el) continue;
const name = `${String(i + 1).padStart(2, "0")}-${config.slides[i].id}-${W}x${H}.png`;
const dataUrl = await captureSlide(el);
tabFolder.file(name, dataUrl.split(",")[1], { base64: true });
}
}
}
}
// 生成并触发ZIP下载
const appSlug = config.app.name.replace(/\s+/g, "-").toLowerCase();
const blob = await zip.generateAsync({ type: "blob" });
saveAs(blob, `${appSlug}-play-store-screenshots.zip`);
}点击一次即可生成,包含按类型和语言组织的所有截图,解压后即可上传到Play Console。
focusflow-play-store-screenshots.zipMulti-Language & RTL Support
多语言与RTL支持
Screenshots organize into locale folders under :
exports/phone/exports/phone/
├── en/
├── de/
└── ar/ ← RTL locale — layouts are mirroredRTL detection and layout mirroring:
javascript
const RTL_LOCALES = new Set(["ar", "he", "fa", "ur"]);
const isRtl = RTL_LOCALES.has(selectedLocale);
// Apply dir attribute to each slide container element:
// slideEl.setAttribute("dir", isRtl ? "rtl" : "ltr");
//
// CSS flexbox respects dir automatically — text and layout mirror
// without needing manual position overrides.When contains multiple entries, build a locale in the controls bar. On change, re-render all preview slides in the selected language.
config.locales<select>截图按语言文件夹组织在下:
exports/phone/exports/phone/
├── en/
├── de/
└── ar/ ← RTL语言 — 布局自动镜像RTL检测和布局镜像:
javascript
const RTL_LOCALES = new Set(["ar", "he", "fa", "ur"]);
const isRtl = RTL_LOCALES.has(selectedLocale);
// 给每个截图容器元素添加dir属性:
// slideEl.setAttribute("dir", isRtl ? "rtl" : "ltr");
//
// CSS flexbox会自动适配dir属性 — 文本和布局自动镜像
// 无需手动调整位置。当包含多个条目时,在控制栏中构建语言选择器。切换时重新渲染所有预览截图为选中的语言。
config.locales<select>QA Checklist
QA检查清单
Check all of these before exporting final assets.
Content
- Each slide communicates exactly one idea — no compound claims with "and"
- Every headline passes the one-second test at arm's length
- No feature is repeated across slides
- The set has a narrative arc: hook → proof → trust → CTA (or similar)
- Subtext (when present) adds information, not repetition
Visual
- All slides use (or
layout: "text-top"for at most one slide) — no other layout values"full-bleed" - Text never overlaps with the phone frame (keep headline short if it risks overflow)
- The feature graphic reads clearly at 50% scale (how it appears in Play Store search results)
Play Store compliance
- Minimum 2 phone screenshots exported
- Maximum 8 phone screenshots per device type
- Feature graphic exported at exactly 1024 × 500px
- All screenshots are JPEG or 24-bit PNG — no alpha channel / transparency in final exports
- All files are under 8MB
- No iOS-style UI elements visible — no Dynamic Island, no iPhone notch shape, no iOS home indicator pill, no iOS status bar style
- No Apple branding, App Store badges, or iOS system iconography anywhere
- Background is fully opaque on all assets
Export quality
- All text is sharp and crisp at full resolution (no blur artifacts)
- Screenshots inside the mockup are not stretched or distorted — maintain original aspect ratio
- The double-call export pattern was used (skipping it causes blurry fonts)
- Filenames are zero-padded (,
01-, not02-,1-)2-
导出最终资产前检查所有项:
内容
- 每个截图页只传达一个想法 — 没有带"和"的复合表述
- 所有标题在一臂距离下一秒就能看懂
- 没有功能在多个截图页重复出现
- 整体有叙事逻辑:吸引注意→价值证明→信任建立→行动号召(或类似逻辑)
- 子文案(如果有)补充信息,不是重复
视觉
- 所有截图页使用(最多一页使用
layout: "text-top") — 没有其他布局值"full-bleed" - 文本永远不会和手机边框重叠(如果有溢出风险就缩短标题)
- 特色图在50%缩放时依然清晰可读(这是Play Store搜索结果中的展示大小)
Play Store合规
- 最少导出2张手机截图
- 每个设备类型最多8张手机截图
- 特色图导出尺寸严格为1024 × 500px
- 所有截图为JPEG或24位PNG — 最终导出文件没有alpha通道/透明度
- 所有文件大小在8MB以下
- 没有可见的iOS风格UI元素 — 没有灵动岛、没有iPhone刘海、没有iOS主页指示器、没有iOS状态栏样式
- 任何地方没有苹果品牌、App Store徽章或iOS系统图标
- 所有资产背景完全不透明
导出质量
- 全分辨率下所有文字清晰锐利(没有模糊 artifacts)
- 样机中的截图没有拉伸或变形 — 保持原始宽高比
- 使用了两次调用导出模式(跳过会导致字体模糊)
- 文件名前补零(、
01-,不是02-、1-)2-
After Export: Uploading to Play Console
导出后:上传到Play Console
- Go to Google Play Console → your app → Store presence → Main store listing
- Under Phone screenshots, upload the files from in order
exports/phone/en/ - Under Feature graphic, upload
feature-graphic-1024x500.png - If tablet screenshots were generated, upload them under the corresponding tablet section
- Save and submit for review
Play Store processes uploads instantly — no waiting for asset approval before you can submit a release.
- 前往Google Play Console → 你的应用 → Store presence(商店展示) → Main store listing(主商店详情页)
- 在**Phone screenshots(手机截图)**下,按顺序上传中的文件
exports/phone/en/ - 在**Feature graphic(特色图)**下,上传
feature-graphic-1024x500.png - 如果生成了平板截图,上传到对应的平板分类下
- 保存并提交审核
Play Store会立即处理上传 — 不需要等待资产审核就能提交版本。