关于 asc-screenshots
asc-screenshots 是一项技能,用于构建生成 iPhone 和 iPad App Store 宣传截图的 Next.js 页面。它指导文案与版式规划,使用 FrameMe 添加设备边框,并按 Apple 指定分辨率导出图像。
主要功能
- 01创建 Next.js 截图生成器
- 02使用 FrameMe 为截图添加设备边框
- 03规划每页一个主题的叙事结构
- 04支持多语言文案
- 05导出 iPhone 和 iPad 图像
适用场景
- 01制作 App Store 宣传图
- 02创建带设备边框的截图版式
- 03生成多语言截图文案和预览
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ keremerkan/ asc-screenshots --skill asc-screenshots安装后在 Claude Code 中输入/asc-screenshots确认已加载。
详细介绍
App Store 截图生成器
概述
构建一个 Next.js 页面,用于生成 iPhone 和 iPad 的 App Store 截图,将其作为广告(而非 UI 展示),并通过 html-to-image 按 Apple 要求的分辨率导出。使用 FrameMe,将应用截图合成到 Apple 官方设备边框中,达到像素级精确度(包括灵动岛)。截图是 App Store 上最重要的转化素材。
核心原则
截图是广告,不是说明文档。 每张截图都要推销一个理念。如果你展示的是 UI,那就做错了——你要推销的是一种感受、一种结果,或是解决一个痛点。
第 1 步:向用户提出以下问题
编写任何代码之前,先向用户提出以下所有问题。收到答复后再继续:
必填
- 应用截图 — “你的应用截图放在哪里?(实际设备截屏的 PNG 文件)”
- 应用图标 — “你的应用图标 PNG 放在哪里?”
- 品牌颜色 — “你的品牌颜色是什么?(强调色、文字颜色、背景偏好)”
- 字体 — “你的应用使用什么字体?(或者你希望截图使用什么字体?)”
- 功能列表 — “按优先级列出应用的功能。你的应用最主要的功能是什么?”
- 幻灯片数量 — “你想要多少张截图?(Apple 允许最多 10 张)”
- 风格方向 — “你想要什么风格?例如:温暖/自然、暗色/忧郁、简洁/极简、大胆/多彩、渐变丰富、扁平化。如果有 App Store 截图参考,也请分享。”
- 设备 — “你是在哪些设备上截取的截图?(例如:iPhone 17 Pro、iPhone 17 Pro Max、iPad Pro 11"、iPad Pro 13" 等)”
可选
- 本地化 — “你想要多种语言的截图吗?如果想要,需要哪些语言?(例如:英语、德语、法语)你会在预览中获得一个语言切换器,以便切换语言。”
- 项目位置 — “你想永久保留截图生成器项目,以便之后回来修改或添加语言吗?如果想保留,应将其创建在哪里?(例如:放在应用的代码仓库中)”
- 组件素材 — “你有想用作浮动装饰的 UI 元素 PNG 吗?(卡片、小组件等)如果没有也没关系——我们会跳过。”
- 其他说明 — “还有什么具体要求、限制或偏好吗?”
根据答复推导(不要询问——自行决定)
根据用户的风格方向、品牌颜色和应用美学,决定:
- 背景风格:渐变方向、颜色,以及采用浅色还是深色基底
- 装饰元素:色块、光晕、几何形状,或不使用装饰——与风格相匹配
- 深色与浅色幻灯片:各自的数量,以及适合深色处理的功能
- 排版处理:字重、字距、行高——与品牌个性相匹配
- 调色板:根据品牌颜色推导文字颜色、辅助色和阴影色调
重要: 如果用户在流程中的任何时候提出其他说明,都要遵循。用户说明始终优先于技能默认设置。
第 2 步:设置项目
要求
- Node.js 18+
- FrameMe — 像素级精确的设备边框渲染必需(包括灵动岛)。通过从源码构建来安装:
如果用户没有 FrameMe,请在继续之前让他们先安装。git clone https://github.com/joshluongo/frameme.git /tmp/frameme cd /tmp/frameme && swift build -c release strip .build/release/frameme cp .build/release/frameme /usr/local/bin/frameme
检测包管理器
检查可用的包管理器,并按以下优先级选择:bun > pnpm > yarn > npm
# Check in order
which bun && echo "use bun" || which pnpm && echo "use pnpm" || which yarn && echo "use yarn" || echo "use npm"
搭建项目骨架(如果没有现成的 Next.js 项目)
# With bun:
bunx create-next-app@latest . --typescript --app --src-dir --no-eslint --import-alias "@/*"
bun add html-to-image jszip
# With pnpm:
pnpx create-next-app@latest . --typescript --app --src-dir --no-eslint --import-alias "@/*"
pnpm add html-to-image jszip
# With yarn:
yarn create next-app . --typescript --app --src-dir --no-eslint --import-alias "@/*"
yarn add html-to-image jszip
# With npm:
npx create-next-app@latest . --typescript --app --src-dir --no-eslint --import-alias "@/*"
npm install html-to-image jszip
文件结构
project/
├── public/
│ ├── app-icon.png # User's app icon
│ ├── framed/ # FrameMe output (device-framed screenshots)
│ │ ├── iphone/
│ │ │ ├── en/ # Per-locale if localized UI screenshots
│ │ │ │ ├── home_framed.png
│ │ │ │ └── ...
│ │ │ └── de/ # (if single language, skip locale dirs)
│ │ │ └── ...
│ │ └── ipad/
│ │ └── ...
│ └── screenshots/ # Raw app screenshots (before framing)
│ ├── iphone/
│ │ ├── en/
│ │ │ ├── home.png
│ │ │ └── ...
│ │ └── de/
│ │ └── ...
│ └── ipad/
│ └── ...
├── src/app/
│ ├── copy.json # Localized copy (editable without touching code)
│ ├── layout.tsx # Font setup
│ └── page.tsx # The screenshot generator (single file)
└── package.json
如果各语言的应用 UI 相同(只有营销文案不同),请跳过语言区域子目录——所有语言共用相同的带设备边框的图片。
生成器由 page.tsx + copy.json 构成。 不需要路由、额外布局或 API 路由。所有文案都放在 copy.json 中,因此用户无需编辑组件即可更新文案。
字体设置
// src/app/layout.tsx
import { YourFont } from "next/font/google"; // Use whatever font the user specified
const font = YourFont({ subsets: ["latin"] });
export default function Layout({ children }: { children: React.ReactNode }) {
return <html><body className={font.className}>{children}</body></html>;
}
步骤 2.5:使用 FrameMe 为截图添加设备边框
构建页面前,先使用 FrameMe 将每张原始截图合成到对应的设备边框中。这样生成的设备图像像素精准,Dynamic Island、圆角和边框都完整保留。
设备边框
此技能包含 Apple 官方设备边框(与此 SKILL.md 位于同一目录)。请使用与用户截取屏幕截图时所用设备匹配的边框:
| 边框文件 | 设备 | 图像尺寸 |
|---|---|---|
iphone-17-pro.png | iPhone 17 Pro(6.1") | 1350×2760 |
iphone-17-pro-max.png | iPhone 17 Pro Max(6.9") | 1470×3000 |
ipad-pro-11.png | iPad Pro 11" M4 | 1880×2640 |
ipad-pro-13.png | iPad Pro 13" M4 | 2300×3000 |
为所有截图添加边框
根据用户对问题 #8 的回答,选择匹配的边框:
SKILL_DIR="<path to this skill's directory>"
# Frame screenshots per locale (en is required, others optional)
for LANG in en de fr; do
# iPhone (pick the matching bezel)
mkdir -p public/framed/iphone/$LANG
frameme "$SKILL_DIR/iphone-17-pro.png" public/screenshots/iphone/$LANG/*.png --output public/framed/iphone/$LANG
# OR: frameme "$SKILL_DIR/iphone-17-pro-max.png" ...
# iPad (pick the matching bezel)
mkdir -p public/framed/ipad/$LANG
frameme "$SKILL_DIR/ipad-pro-11.png" public/screenshots/ipad/$LANG/*.png --output public/framed/ipad/$LANG
# OR: frameme "$SKILL_DIR/ipad-pro-13.png" ...
done
如果应用 UI 不随语言变化(只有宣传文案不同),则只需在 en/ 下处理一次。Device 组件的 onError 回退机制会为所有语言版本使用英文图像。
FrameMe 会自动:
- 根据边框的 alpha 通道检测屏幕区域
- 创建逐扫描线遮罩(处理 Dynamic Island 和圆角)
- 将截图合成到边框后方
- 输出
filename_framed.png
重要说明
- 边框必须与截图设备匹配 — 使用不匹配的边框会导致裁切或错位
- FrameMe 不会调整尺寸 — 截图应与边框的屏幕区域匹配
- page.tsx 将引用添加边框后的图像 — 用户在广告布局中看到的就是最终设备外观
步骤 3:规划幻灯片
截图框架(叙事弧线)
根据用户要求的幻灯片数量调整此框架。不必包含所有版位——选择合适的内容:
| 版位 | 用途 | 备注 |
|---|---|---|
| #1 | 主视觉 / 核心益处 | 应用图标 + 标语 + 主屏幕。这是大多数人唯一会看到的内容。 |
| #2 | 差异化优势 | 此应用相较竞争对手的独特之处 |
| #3 | 生态系统 | 小组件、扩展、手表——主应用之外的内容。不适用时跳过。 |
| #4+ | 核心功能 | 每张幻灯片介绍一项功能,按重要性排序 |
| 倒数第二张 | 信任信号 | 身份 / 工艺——“为 [X] 类人群打造” |
| 最后一张 | 更多功能 | 用胶囊标签列出其他功能和即将推出的内容。功能较少时跳过。 |
规则:
- 每张幻灯片只推介一个想法。绝不要在一张幻灯片上介绍两项功能。
- 各张幻灯片的布局应有所变化——不要重复相同的模板结构。
- 加入 1–2 张对比幻灯片(反转背景),营造视觉节奏。
步骤 4:先撰写文案
开始构建布局前,先让所有标题获得认可。糟糕的文案会毁掉出色的设计。
铁律
- 每个标题只表达一个想法。 绝不要用“和”连接两件事。
- 使用简短、常见的词语。 每个词 1–2 个音节。除非属于特定领域,否则不要使用术语。
- 每行 3–5 个词。 在 App Store 的缩略图尺寸下也必须清晰易读。
- 换行要有意为之。 使用
<br />控制换行位置。
三种写法(每张幻灯片选一种)
| 类型 | 作用 | 示例 |
|---|---|---|
| 描绘一个瞬间 | 让人想象自己正在使用 | “无需打开应用,也能查看咖啡。” |
| 陈述结果 | 描述使用后生活的样子 | “为你买的每杯咖啡安个家。” |
| 消除痛点 | 指出问题并彻底解决 | “再也不会浪费一包好咖啡。” |
永远行不通的写法
- 将功能列表当作标题:“用标签、类别和备注记录每件物品”
- 用“和”连接两个想法:“追踪 X,不再错过 Y”
- 复合从句:“保存并自定义你拥有的每个 Y 的 X”
- 空泛的美好愿景:“追踪每件物品”
- 营销流行语:“AI 驱动的建议”(除非确实使用了 AI)
文案编写流程
- 使用三种方法,为每张幻灯片写出 3 个选项
- 将每个选项拿在手臂长度之外阅读——如果无法在 1 秒内看懂,就说明太复杂
- 检查:每行是否有 3–5 个词?如果没有,请调整换行
- 向用户展示选项,并说明每个选项的理由
- 如需本地化:将获批的标题翻译成所有指定语言。注意符合各语言的语气和正式程度。各语言的换行可能需要调整——德语和法语文本通常比英语更长。
文案风格参考应用
- Raycast — 具体、描述性,每张幻灯片传达一个明确价值
- Turf — 极简的动作动词,语气自然
- Mela / Notion — 温暖、简约、优雅
步骤 5:构建页面
架构
page.tsx
├── Constants (IPHONE_W, IPHONE_H, IPAD_W, IPAD_H, design tokens)
├── LOCALES (code, label, asc locale code for App Store Connect)
├── ASC_DEVICE (maps "iphone" → "APP_IPHONE_67", "ipad" → "APP_IPAD_PRO_3GEN_129")
├── COPY (imported from copy.json — localized strings per language, keyed by slide name)
├── Device component (renders a pre-framed device image — just an <img>)
├── Caption component (label + headline)
├── Decorative components (blobs, glows, shapes — based on style direction)
├── Screenshot1..N components (one per slide, with iPhone and/or iPad variants)
├── SCREENSHOTS array (registry, with device type tag)
├── ScreenshotPreview (ResizeObserver scaling + hover export)
└── ScreenshotsPage (grid + toolbar with language switcher + device filter + Export/Export All Languages)
导出尺寸(仅最大尺寸 — Apple 会自动缩放至较小显示屏)
App Store Connect 接受每台设备的最大截图尺寸,并自动缩放至较小显示屏。只需生成:
// iPhone 6.9" — covers all iPhone sizes
const IPHONE_W = 1320;
const IPHONE_H = 2868;
// iPad 13" — covers all iPad sizes
const IPAD_W = 2064;
const IPAD_H = 2752;
渲染策略
每张截图都按完整导出分辨率设计。会生成两个副本:
- 预览:通过 ResizeObserver 使用 CSS
transform: scale(),以适配网格卡片 - 导出:在屏幕外以真实分辨率渲染,使用
position: absolute; left: -9999px
如果同时请求 iPhone 和 iPad:
- 分开展示区域:将 iPhone 和 iPad 渲染在不同区域,并添加设备标题(
<h2>iPhone</h2>、<h2>iPad</h2>),每个区域使用自己的网格。不要将两者混在同一个网格中。 - 设备筛选器:工具栏包含 All / iPhone / iPad 切换按钮,用于显示或隐藏对应区域。
- 每个区域使用
SCREENSHOTS.filter(s => s.device === "iphone")等方式进行筛选。
本地化
如果用户需要多种语言,请将所有文案存储在单独的 copy.json 文件中,这样用户无需修改代码即可编辑文本:
src/app/copy.json
{
"en": {
"iphone-hero": { "headline": "Spam texts,<br/>silenced.", "pills": ["Smart", "Private"] },
"iphone-privacy": { "label": "On-device AI", "headline": "Completely<br/>private.", "sub": "..." }
},
"de": {
"iphone-hero": { "headline": "Spam-SMS,<br/>verstummt.", "pills": ["Intelligent", "Datenschutz"] },
"iphone-privacy": { "label": "KI auf dem Gerät", "headline": "Vollständig<br/>privat.", "sub": "..." }
}
}
在 page.tsx 中导入并进行类型转换:
import COPY_JSON from "./copy.json";
const COPY = COPY_JSON as Record<Locale, Record<string, SlideCopy>>;
每个幻灯片组件都会接收当前区域设置,并从 COPY[locale][slideName] 中查找对应文案。工具栏包含一个语言切换器(<select> 下拉菜单),用于更新共享的 locale 状态。选项以 {label} ({code}) 的格式显示(例如 "English (en)"),并按标签字母顺序排序。这样用户就可以在导出前,以任意语言预览所有幻灯片。
导出时,请将区域设置加入文件名:01-iphone-hero-en-1320x2868.png、01-iphone-hero-de-1320x2868.png。“Export All”按钮会导出当前选中的语言。如果用户需要所有语言,则需逐一切换并导出。
**本地化设备图片:**如果应用截图因语言而异(例如 UI 已本地化),请按区域设置整理带设备边框的图片:public/framed/iphone/en/、public/framed/iphone/de/ 等。幻灯片组件会在路径中使用当前区域设置:src={/framed/iphone/${locale}/iPhone-01_framed.png}。英语(en)为必需的默认语言;其他语言均为可选,如果缺少对应图片,则回退到英语图片(通过 Device 组件上的 onError 实现)。
设备组件
由于 FrameMe 会生成包含完整设备边框(边框 + 截图合成)的图片,设备组件只需一个 <img> 标签——无需 CSS 裁剪、无需计算屏幕偏移,也无需使用圆角技巧:
function Device({ src, alt, style }: {
src: string; alt: string; style?: React.CSSProperties;
}) {
return (
<img
src={src}
alt={alt}
style={style}
draggable={false}
onError={(e) => {
// Fall back to English if localized image is missing
const img = e.currentTarget;
const enSrc = img.src.replace(/\/(?!en\/)[a-z]{2}\//, "/en/");
if (img.src !== enSrc) img.src = enSrc;
}}
/>
);
}
带设备边框的图片已包含设备边框、Dynamic Island、圆角和像素级合成的屏幕内容。设备周围的透明区域使其能够自然地显示在任意背景上。onError 回退机制可确保缺少特定语言图片时,优雅地回退到英语图片。
排版(与分辨率无关)
所有尺寸均相对于画布宽度 W(根据设备使用 IPHONE_W 或 IPAD_W):
| 元素 | 尺寸 | 字重 | 行高 |
|---|---|---|---|
| 类别标签 | W * 0.028 | 600(半粗体) | 默认 |
| 标题 | W * 0.09 到 W * 0.1 | 700(粗体) | 1.0 |
| 主视觉标题 | W * 0.1 | 700(粗体) | 0.92 |
设备摆放模式
在不同幻灯片间交替使用 — 切勿连续两次使用相同布局:
居中设备(主视觉、单项功能):
bottom: 0, width: "82-86%", translateX(-50%) translateY(12-14%)
两台设备叠放(对比):
Back: left: "-8%", width: "65%", rotate(-4deg), opacity: 0.55
Front: right: "-4%", width: "82%", translateY(10%)
设备 + 浮动元素(仅限用户提供了组件 PNG 时):
Cards should NOT block the device's main content.
Position at edges, slight rotation (2-5deg), drop shadows.
If distracting, push partially off-screen or make smaller.
iPad 专属布局说明
iPad 截图的宽高比更接近横向(2064×2752,接近 3:4,而 iPhone 约为 1:2.2)。这会影响布局:
- 设备图片相对于画布更宽 — 使用更窄的宽度(70-78%,而不是 82-86%),以免显得拥挤
- 说明文字可能需要放在设备上方而不是旁边,因为设备更宽,留给横向排布的空间更少
- 两台设备的布局采用更小的缩放比例和更大的重叠效果时效果最好
- iPad 更大的可视屏幕区域意味着应用 UI 更易读 — 可以多展示内容
“更多功能”幻灯片(可选)
使用深色/高对比度背景,搭配应用图标、标题(“还有更多精彩功能。”)和功能标签。可以加入“即将推出”部分,并使用更暗淡的标签。
第 6 步:导出
为什么使用 html-to-image,而不是 html2canvas
html2canvas 在处理 CSS 滤镜、渐变、drop-shadow、backdrop-filter 和复杂裁剪时会出错。html-to-image 使用原生浏览器 SVG 序列化 — 能忠实处理所有 CSS。
导出实现
import { toPng } from "html-to-image";
// CRITICAL: el must be the slide component's root div (position: relative),
// NOT the offscreen wrapper. Use ref on wrapper, then ref.firstElementChild.
// toPng must capture the element that owns the positioned children.
const wrapper = el.parentElement!;
// Before capture: move wrapper on-screen
wrapper.style.left = "0px";
wrapper.style.opacity = "1";
wrapper.style.zIndex = "-1";
const opts = { width: W, height: H, pixelRatio: 1, cacheBust: true };
// CRITICAL: Double-call trick — first warms up fonts/images, second produces clean output
await toPng(el, opts);
const dataUrl = await toPng(el, opts);
// After capture: move wrapper back off-screen
wrapper.style.left = "-9999px";
wrapper.style.opacity = "";
wrapper.style.zIndex = "";
iPhone 幻灯片使用 W = IPHONE_W, H = IPHONE_H,iPad 幻灯片使用 W = IPAD_W, H = IPAD_H。
离屏容器设置:包装器 div 使用 position: absolute; left: -9999px,并设置 fontFamily。ref 应指向其中幻灯片的根 div(通过 firstElementChild),而不是包装器本身:
ref={(el) => { if (el?.firstElementChild) exportRefs.current.set(i, el.firstElementChild as HTMLDivElement); }}
关键规则
- 双重调用技巧:第一次调用
toPng()会延迟加载字体/图片。第二次调用才会生成干净的输出。否则,导出结果会是空白。 - 捕获时保持屏幕内显示:调用
toPng前,暂时将位置移至left: 0。 - 离屏包装器:使用
position: absolute; left: -9999px(而不是fixed)。不要为包装器设置width/height— 让幻灯片组件定义自己的尺寸。 - 无需调整尺寸:每张幻灯片都已采用精确的导出分辨率。无需执行画布缩放步骤。
- Zip 导出(兼容 asc-client):提供两个导出按钮。“导出 ({LOCALE})”导出当前语言。“导出所有语言”会遍历每种语言(设置状态、等待重新渲染、捕获)。两者都会生成与 asc-client 兼容的 zip 文件。结构如下:
设备类型映射:en-US/APP_IPHONE_67/01_hero.png en-US/APP_IPAD_PRO_3GEN_129/01_hero.png de-DE/APP_IPHONE_67/01_hero.pngiphone → APP_IPHONE_67、ipad → APP_IPAD_PRO_3GEN_129。每个语言条目都需要一个asc字段,用于指定 App Store Connect 语言代码(例如{ code: "en", label: "English", asc: "en-US" })。文件按字母顺序排列,因此请使用带前导零的前缀:01_name.png。 - 单击导出:下载单独的 PNG 文件,文件名中包含设备信息:
01-iphone-hero-en-1320x2868.png。 - 连续导出之间延迟 300ms。
- 在离屏容器上设置
fontFamily。 - 使用
String(index + 1).padStart(2, "0")进行编号。
常见错误
| 错误 | 修复方法 |
|---|---|
| 所有幻灯片看起来都一样 | 改变设备的位置(居中、靠左、靠右、两个设备、不放设备) |
| 装饰元素看不见 | 增大尺寸并提高不透明度——宁可太显眼,也不要看不见 |
| 文案太复杂 | 进行“手臂伸直时一秒读完”测试 |
| 浮动元素挡住设备 | 将其移到屏幕边缘之外或设备上方 |
| 背景纯白或纯黑 | 使用渐变——即使很微妙,也能增加层次感 |
| 画面太杂乱 | 移除浮动元素,简化为设备加标题 |
| 画面太简单或空荡 | 增大装饰元素,或在边缘添加浮动物品 |
| 标题中用了“和” | 拆成两张幻灯片,或只选一个要点 |
| 幻灯片之间缺乏视觉对比 | 混用浅色和深色背景 |
| 导出结果为空白 | 使用双重调用技巧;捕获前将元素移到屏幕内 |
| 导出结果顶部空白、内容在底部 | toPng 必须指定幻灯片的根 div(带有 position: relative),而不是屏幕外的包装元素 |
| 未安装 FrameMe | 从源码构建:克隆仓库,运行 swift build -c release,将二进制文件复制到 PATH |
| 添加边框后的图像效果不对 | 确保原始屏幕截图与边框的屏幕分辨率一致 |