mcpField
S
screen-recording

screen-recording

github.com/github创意与媒体40.5k152 次安装1 天前更新
代码仓库

关于 screen-recording

Screen Recording 是用于制作 UI 交互、桌面应用和工作流动画 GIF 演示的技能。它结合画面捕获、时长控制与注释,适用于文档、PR 和版本说明。

主要功能

  1. 01使用 Playwright 捕获浏览器画面
  2. 02使用 mss 录制桌面区域
  3. 03使用 imageio 组装 GIF
  4. 04设置可变帧时长
  5. 05添加注释及淡入效果

适用场景

  1. 01在 PR 中展示多步 UI 流程
  2. 02记录行为变化前后对比
  3. 03演示错误复现或修复过程
  4. 04制作带注释的版本发布演示

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/awesome-copilot --skill screen-recording

安装后在 Claude Code 中输入/screen-recording确认已加载。

详细介绍

屏幕录制

创建动态 GIF 演示,展示某项功能或工作流的实际运行效果——配有标注、可变时长和恰当的节奏。适用于 PR 描述、文档和发布说明。

何时使用本技能

当你需要以下操作时,可使用本技能:

  • 将多步骤 UI 交互录制为动态 GIF
  • 创建展示前后行为对比的演示
  • 为文档或发布说明构建带标注的逐步讲解
  • 展示缺陷复现或修复的实际过程

前置条件

pip install playwright Pillow imageio numpy scipy mss -q
playwright install chromium

核心工作流

1. 捕获帧

使用 Playwright 逐步执行交互并捕获每一帧:

from playwright.async_api import async_playwright

async def record_frames(url, steps, width=1400, height=900):
    """
    steps: list of dicts with 'action' (async callable taking page)
           and 'name' (frame filename)
    """
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": width, "height": height})
        await page.goto(url, wait_until="networkidle")

        for step in steps:
            if step.get("action"):
                await step["action"](page)
                await page.wait_for_timeout(step.get("wait", 500))
            await page.screenshot(path=step["name"])

        await browser.close()
2. 用 imageio 合成 GIF

写入 GIF 时请使用 imageio,而不是 PIL——PIL 的 GIF 编码器会合并视觉上相似的帧,从而毁掉动画。

import imageio.v3 as iio
from PIL import Image
import numpy as np

frames = []
durations = []

for frame_path, duration_ms in frame_list:
    img = Image.open(frame_path)
    frames.append(np.array(img))
    durations.append(duration_ms)

iio.imwrite("demo.gif", frames, duration=durations, loop=0)
3. 可变帧时长

统一的时长会让一切要么显得太快,要么显得太慢。请使用可变时长:

阶段时长原因
快速动作(输入、点击)100ms感觉自然,保持节奏感
动作后的停顿600-800ms让观看者消化刚刚发生的事
主视觉/最终信息500ms+核心要点需要时间被理解
4. 为帧添加标注

使用 image-annotations 技能将标注应用到特定帧:

from PIL import Image, ImageDraw, ImageFont

def annotate_frame(frame_path, annotations, out_path):
    img = Image.open(frame_path)
    draw = ImageDraw.Draw(img)

    for ann in annotations:
        # Apply annotation (rect, arrow, label, etc.)
        pass

    img.save(out_path)
5. 淡入标注

实现标注的平滑出现效果:

def apply_fade(base_frame, annotation_layer, alpha):
    """Blend annotation onto frame at given alpha (0.0 to 1.0)"""
    blended = Image.blend(
        base_frame.convert("RGBA"),
        annotation_layer.convert("RGBA"),
        alpha
    )
    return blended.convert("RGB")

# 2-frame pop-in at 10fps: 50% then 100%
faded_frames = [
    apply_fade(base, annotations, 0.5),  # frame 1: half opacity
    apply_fade(base, annotations, 1.0),  # frame 2: full opacity
]

在 10fps 下,使用 2 个淡入帧(共 0.2s)。在 30fps 下,使用 3-4 帧。缓动曲线在低 FPS 下效果不佳——简单的弹出式出现更利落,也更易读。

以脚本形式构建

一旦超出简单演示的范围,标注逻辑就会变得复杂。请编写一个专用脚本(例如 annotate_gif.py),用函数组织代码,而不是内联编写。你会反复调整时长和位置。

测试动画

务必先单独测试——不要为了测试一处淡入调整而重新构建整个演示:

# Small test GIF: 10 bare frames → fade frames → 15 hold frames
# Add a frame counter overlay for debugging:
draw.text((10, height - 30), f"F{i}/{total} a={alpha:.0%} FADE",
          fill="white", font=small_font)

桌面屏幕录制(mss)

用于录制桌面应用、终端或浏览器之外的任何内容。使用 mss 进行快速屏幕捕获。

import mss
from PIL import Image
import time

def record_gif(output_path, region=None, duration=5, fps=8):
    """Record screen region to GIF. region = {left, top, width, height} or None for full screen."""
    with mss.mss() as sct:
        if region is None:
            region = sct.monitors[1]  # primary monitor

        frames = []
        t_end = time.time() + duration
        while time.time() < t_end:
            t0 = time.time()
            shot = sct.grab(region)
            frames.append(Image.frombytes('RGB', shot.size, shot.rgb))
            time.sleep(max(0, 1 / fps - (time.time() - t0)))

    frames[0].save(output_path, save_all=True, append_images=frames[1:],
                   duration=int(1000 / fps), loop=0, optimize=True)
    return len(frames)

record_gif('demo.gif', region={'left': 0, 'top': 0, 'width': 800, 'height': 500}, duration=3)

已测试:3s 时长、8fps → 24 帧,约 31KB。将 fps 保持在 ≤ 10 以获得合理的文件大小。

注意: PIL.save(save_all=True) 适用于简单录制,但会合并视觉上相似的帧。对于带淡入效果的标注 GIF,请改用 imageio.v3.imwrite。

结合窗口捕获
# Find window rect, then record it as a GIF
# Reuse find_window() from the ui-screenshots skill
import ctypes
from ctypes import c_int, Structure, byref, windll

class RECT(Structure):
    _fields_ = [('left', c_int), ('top', c_int), ('right', c_int), ('bottom', c_int)]

hwnd = find_window('My App')[0][0]
rect = RECT()
windll.user32.GetWindowRect(hwnd, byref(rect))
region = {'left': rect.left, 'top': rect.top,
          'width': rect.right - rect.left, 'height': rect.bottom - rect.top}
record_gif('app-demo.gif', region=region, duration=5, fps=8)

基于差异的聚类检测

以编程方式找出帧之间发生变化的区域,从而决定要标注什么:

import numpy as np
from scipy import ndimage

def find_changed_clusters(frame_a, frame_b, threshold=30, min_pixels=300, dilate=5):
    """Find bounding boxes of changed regions between two frames."""
    diff = np.abs(frame_b.astype(float) - frame_a.astype(float)).max(axis=2)
    mask = diff > threshold
    dilated = ndimage.binary_dilation(mask, iterations=dilate)
    labeled, n = ndimage.label(dilated)
    clusters = []
    for i in range(1, n + 1):
        ys, xs = np.where(labeled == i)
        if len(ys) < min_pixels:
            continue
        clusters.append((xs.min(), ys.min(), xs.max(), ys.max(), len(ys)))
    return sorted(clusters, key=lambda c: -c[4])  # largest first

格式兼容性

格式VS Code 预览GitHub浏览器
GIF✅ 可动画✅✅
WebP⚠️ 仅静态✅✅
MP4❌ 无法显示⚠️✅

GIF 是唯一在 VS Code 预览、GitHub markdown 和浏览器中普遍支持动态效果的格式。

指南

  1. 输入 → 停顿 → 标注——快速动作期间,不显示任何标注。先停顿,再标注
  2. 主视觉信息使用最大字号——核心要点用 64pt+,细节用 38pt
  3. GIF 调色板不会毁掉渐变——256 色调色板可保留 20 个不同的 alpha 层级
  4. 输入/交互最低 10fps——更低会显得卡顿
  5. 迭代式构建——先把帧序列做对,其次添加标注,最后调整时长

限制

  • GIF 每帧仅限 256 色——对于 UI 截图足够,但在照片类内容上可能出现色带
  • 大型 GIF(高分辨率下 50+ 帧)可能达到数 MB——考虑裁剪到相关区域
  • GIF 不支持音频——旁白演示请使用 MP4(但会失去 VS Code 预览支持)

评论

支持 Markdown