mcpField
A
asc-screenshots

asc-screenshots

github.com/keremerkan营销与增长7846 次安装32 周前更新
代码仓库

关于 asc-screenshots

asc-screenshots 是一项技能,用于构建生成 iPhone 和 iPad App Store 宣传截图的 Next.js 页面。它指导文案与版式规划,使用 FrameMe 添加设备边框,并按 Apple 指定分辨率导出图像。

主要功能

  1. 01创建 Next.js 截图生成器
  2. 02使用 FrameMe 为截图添加设备边框
  3. 03规划每页一个主题的叙事结构
  4. 04支持多语言文案
  5. 05导出 iPhone 和 iPad 图像

适用场景

  1. 01制作 App Store 宣传图
  2. 02创建带设备边框的截图版式
  3. 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 步:向用户提出以下问题

编写任何代码之前,先向用户提出以下所有问题。收到答复后再继续:

必填
  1. 应用截图 — “你的应用截图放在哪里?(实际设备截屏的 PNG 文件)”
  2. 应用图标 — “你的应用图标 PNG 放在哪里?”
  3. 品牌颜色 — “你的品牌颜色是什么?(强调色、文字颜色、背景偏好)”
  4. 字体 — “你的应用使用什么字体?(或者你希望截图使用什么字体?)”
  5. 功能列表 — “按优先级列出应用的功能。你的应用最主要的功能是什么?”
  6. 幻灯片数量 — “你想要多少张截图?(Apple 允许最多 10 张)”
  7. 风格方向 — “你想要什么风格?例如:温暖/自然、暗色/忧郁、简洁/极简、大胆/多彩、渐变丰富、扁平化。如果有 App Store 截图参考,也请分享。”
  8. 设备 — “你是在哪些设备上截取的截图?(例如:iPhone 17 Pro、iPhone 17 Pro Max、iPad Pro 11"、iPad Pro 13" 等)”
可选
  1. 本地化 — “你想要多种语言的截图吗?如果想要,需要哪些语言?(例如:英语、德语、法语)你会在预览中获得一个语言切换器,以便切换语言。”
  2. 项目位置 — “你想永久保留截图生成器项目,以便之后回来修改或添加语言吗?如果想保留,应将其创建在哪里?(例如:放在应用的代码仓库中)”
  3. 组件素材 — “你有想用作浮动装饰的 UI 元素 PNG 吗?(卡片、小组件等)如果没有也没关系——我们会跳过。”
  4. 其他说明 — “还有什么具体要求、限制或偏好吗?”
根据答复推导(不要询问——自行决定)

根据用户的风格方向、品牌颜色和应用美学,决定:

  • 背景风格:渐变方向、颜色,以及采用浅色还是深色基底
  • 装饰元素:色块、光晕、几何形状,或不使用装饰——与风格相匹配
  • 深色与浅色幻灯片:各自的数量,以及适合深色处理的功能
  • 排版处理:字重、字距、行高——与品牌个性相匹配
  • 调色板:根据品牌颜色推导文字颜色、辅助色和阴影色调

重要: 如果用户在流程中的任何时候提出其他说明,都要遵循。用户说明始终优先于技能默认设置。

第 2 步:设置项目

要求
  • Node.js 18+
  • 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
    
    如果用户没有 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.pngiPhone 17 Pro(6.1")1350×2760
iphone-17-pro-max.pngiPhone 17 Pro Max(6.9")1470×3000
ipad-pro-11.pngiPad Pro 11" M41880×2640
ipad-pro-13.pngiPad Pro 13" M42300×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. 使用简短、常见的词语。 每个词 1–2 个音节。除非属于特定领域,否则不要使用术语。
  3. 每行 3–5 个词。 在 App Store 的缩略图尺寸下也必须清晰易读。
  4. 换行要有意为之。 使用 <br /> 控制换行位置。
三种写法(每张幻灯片选一种)
类型作用示例
描绘一个瞬间让人想象自己正在使用“无需打开应用,也能查看咖啡。”
陈述结果描述使用后生活的样子“为你买的每杯咖啡安个家。”
消除痛点指出问题并彻底解决“再也不会浪费一包好咖啡。”
永远行不通的写法
  • 将功能列表当作标题:“用标签、类别和备注记录每件物品”
  • 用“和”连接两个想法:“追踪 X,不再错过 Y”
  • 复合从句:“保存并自定义你拥有的每个 Y 的 X”
  • 空泛的美好愿景:“追踪每件物品”
  • 营销流行语:“AI 驱动的建议”(除非确实使用了 AI)
文案编写流程
  1. 使用三种方法,为每张幻灯片写出 3 个选项
  2. 将每个选项拿在手臂长度之外阅读——如果无法在 1 秒内看懂,就说明太复杂
  3. 检查:每行是否有 3–5 个词?如果没有,请调整换行
  4. 向用户展示选项,并说明每个选项的理由
  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;
渲染策略

每张截图都按完整导出分辨率设计。会生成两个副本:

  1. 预览:通过 ResizeObserver 使用 CSS transform: scale(),以适配网格卡片
  2. 导出:在屏幕外以真实分辨率渲染,使用 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.028600(半粗体)默认
标题W * 0.09 到 W * 0.1700(粗体)1.0
主视觉标题W * 0.1700(粗体)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.png
    
    设备类型映射:iphone → 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
添加边框后的图像效果不对确保原始屏幕截图与边框的屏幕分辨率一致

评论

支持 Markdown