mcpField
H
hand-drawn-cartoon

hand-drawn-cartoon

github.com/phantomtonyt创意与媒体2152 次安装3 周前更新
代码仓库

关于 hand-drawn-cartoon

用于规划并制作 HyperFrames 手绘卡通短片的 Skill,涵盖创意简报、节拍安排、动画风格、渲染与检查,并指导制作循环视频。

主要功能

  1. 01创意简报与节拍规划
  2. 02可配置画幅、时长和基调
  3. 03SVG 与 GSAP 手绘动画指南
  4. 04文字可读性与动作时序规则
  5. 05快照检查和渲染清单

适用场景

  1. 01围绕一个笑点或创意制作短片
  2. 02按品牌规范调整角色配色
  3. 03规划节拍和屏幕文字
  4. 04检查并渲染循环动画

安装

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

$npx skills add https://github.com/phantomtonyt/motion-design-skills --skill hand-drawn-cartoon

安装后在 Claude Code 中输入/hand-drawn-cartoon确认已加载。

详细介绍

手绘卡通

一个小角色在纸面舞台上演绎一个点子。线条带有墨迹感,并微微鲜活,仿佛每隔几帧就重新描画一次。整体应当有手作般的温暖与趣味,就像一页会动的童话书。

选项

选项默认值说明
formatvertical 1080×1920或横向 1920×1080、正方形 1080×1080
duration15 s10–45 s 效果最佳
toneplayfulcalm(停顿更长,缓动更柔和)、playful(默认)、slapstick(更夸张的挤压变形、更快的笑点、更多切镜)
charactera simple round character或使用用户自己的角色(匹配其轮廓和颜色)

用户可以用日常语言提出这些要求:“竖屏,20 秒,滑稽夸张”。

简述(规划前回答)

  1. 用一句话说明这个点子或笑点是什么?
  2. 角色是谁,它想要什么?
  3. 首先出了什么岔子,转折或笑点是什么?
  4. 第一秒屏幕上出现什么(钩子)?
  5. 视频最后以哪句台词或标题收尾?

然后写一个节拍列表:时间、画面内容、角色动作、文字和声音。各段时长相加应等于目标时长。默认结构:钩子(1.5 s)、尝试并失败、点子、再次尝试、笑点、结束卡。

阅读清单(与节拍列表一同编写)

为每个节拍写出观众必须理解的一件事(“机器人不会飞”“缓存未命中”),以及这条信息需要清晰呈现多久。一次只呈现一条信息。文字时长下限:标签至少停留 0.8 s;句子完整显示后,每个词约需 0.3 s。如果文字无法在规定时长内读完,就删减文字,不要加快呈现速度。

动作规范

  • 动作应有因果关系:角色会对画面中的事物做出反应,每个动作都有起因和结果。
  • 角色绝不能静止不动:等待时也要呼吸、眨眼和调整重心。这些待机动作本身就是表演,而非填充内容,因此要小幅且不规则。
  • 避免动作同步:双臂不要像镜像一样移动,眼睛、触角和身体也不要在同一帧开始动作。各部分错开 2–4 帧。
  • 过冲和挤压变形应来自角色本身(落地、弹跳、惊讶时)。镜头和文字则使用平滑的缓出效果。
  • 每个场景只保留一个主要移动方向,并在场景切换时让角色或道具延续移动,不要交叉淡化。
  • 在笑点到来前加入 0.3–0.75 s 的定格,让笑点更有冲击力。

风格规则(始终遵守)

  1. 每条线都使用深暖色墨迹(近似 #2b1d16),在 1080 宽度下为 5–8 px,并使用圆角连接。绝不用纯黑。
  2. 线条应有轻微抖动:整个墨迹层使用 3 种略有差异的位移滤镜之一,每 2 帧(12 fps)切换一次。没有这种抖动,画面就会像干净的矢量图。
  3. 始终呈现纸张质感:暖白色底色、淡淡的颗粒叠层和柔和的暗角。
  4. 阴影使用蜡笔排线或一种纯色深色调。角色上不要使用渐变。
  5. 角色应圆润简洁:大头或大身体、点状或椭圆形眼睛并带白色高光、小嘴和短小四肢。动作要带动整个身体。
  6. 每个动作都要先有预备动作,然后移动,再在接触时挤压变形,最后稳定下来。

默认值与覆盖规则

优先级:用户的品牌规范或规格,其次是明确要求,最后才是这些默认值。如果用户提供了品牌色,就将其用于角色和一种强调色。墨迹、纸张和颗粒效果保持不变。

  • 纸张 #efe6d2,浅色纸张 #fbf6ea,墨迹 #2b1d16,强调色 #b8402f(标题、关键道具),天空晕染色 #b9d8e8,太阳或点子黄色 #f5c842,角色颜色 #7fb6c9。
  • 字体:标题使用 Caveat Brush,标签使用 Patrick Hand(Google Fonts)。9:16 画面中的标题字号为 80–110 px。

设置(仅需一次)

node -v && ffmpeg -version
npx hyperframes init my-video --non-interactive --resolution portrait && cd my-video   # landscape for 16:9

在现有空文件夹中使用 npx hyperframes init . --non-interactive --resolution portrait,然后将 index.html 替换为你的合成作品。

制作路线

需求HyperFrames 中的 SVG + GSAP(默认)p5.js + p5.brush(画布)
带抖动效果的墨线轮廓是(置换滤镜)是(重新设定种子的笔刷抖动)
水彩晕染和干笔纹理近似实现(颗粒、排线)是
无 GPU 时快速渲染是较慢
带有命名且可复用部件的角色是更难

默认使用 SVG + GSAP。只有当真实的颜料晕染是重点时才使用 p5.brush;这时,每一帧都要根据时间 t 绘制(不使用未固定种子的随机数)。

视觉语言

  • **场景:**地面线、屋顶或桌沿,搭配一两个道具,以及天空或墙面。形状应略显不规则(使用手绘路径,而不是完美的矩形)。
  • **景深:**设置远景层(太阳、云、山丘)、中景层(场景)和近景层(道具或从镜头前掠过的云)。通过移动世界群组来移动镜头;远景层移动幅度更小。
  • **角色部件:**将身体、眼睛、嘴巴、左右手臂以及触角或耳朵分别放在独立群组中,让它们各自进行动作。使用包裹群组(<g transform="scale(1.7)">)调整角色大小,并在外层群组上为 x、y 和旋转添加动画。缩放动画群组本身会导致位置偏移,因为 SVG 变换默认以左上角为原点。
  • **挤压与拉伸:**以脚部为中心,并将 svgOrigin 设为角色自身坐标中的脚部位置(例如 svgOrigin: "0 6")。保持体积不变:高度增加 10% 时,宽度应缩窄约 5%。
  • **效果:**尘土团(通过动画增大圆的 r 属性并淡出)、绕圈的眩晕星星、速度线、闪光。所有这些效果也都要画上墨线。
动作时长
预备挤压0.1–0.12 s
向上跳0.4–0.5 s,power2.out
顶点处的漫画式停留0.3–0.5 s
下落0.25 s,power3.in
落地挤压0.08 s,然后在 0.5 s 内弹性回稳
眨眼0.12 s,每 2–4 s 一次,间隔不规则
歪头0.3 s,8–12°

让画面引人注目

  • 第一帧就是完整画面:场景、角色、天空,以及手绘卡片上的标题。
  • 每个节拍都要有东西在动:鸟、缓慢转动的太阳、飘动的云、晃动的触角、眨眼。
  • 用镜头呈现一个重要时刻:跟随角色向上或向侧面移动,并配合视差层。
  • 用一种强调色突出关键道具和标题中的关键词,其他部分则采用柔和色调。
  • 笑料要能从剪影中看懂:失败时要有完整的挤压效果,并伴随尘土和星星;成功时则要摆出清晰醒目的姿势。
  • 最后一帧要与第一帧一致(取景和姿势相同),这样视频才能循环播放。
  • 每一帧定格画面都应值得发布。如果截图看起来空洞或平淡,就说明还没做好。

入门模板

<div id="root" data-composition-id="main" data-start="0" data-duration="15" data-width="1080" data-height="1920">
<svg viewBox="0 0 1080 1920" width="1080" height="1920">
  <defs>
    <filter id="boil0"><feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" seed="2"/><feDisplacementMap in="SourceGraphic" scale="4"/></filter>
    <filter id="boil1"><feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" seed="9"/><feDisplacementMap in="SourceGraphic" scale="4"/></filter>
    <filter id="boil2"><feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" seed="17"/><feDisplacementMap in="SourceGraphic" scale="4"/></filter>
    <filter id="grain"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="3" seed="5"/>
      <feColorMatrix type="matrix" values="0 0 0 0 .3  0 0 0 0 .24  0 0 0 0 .16  0 0 0 .2 0"/></filter>
    <pattern id="hatch" width="14" height="14" patternUnits="userSpaceOnUse" patternTransform="rotate(35)"><line x1="0" y1="0" x2="0" y2="14" stroke="#2b1d16" stroke-width="3" stroke-opacity=".28"/></pattern>
  </defs>
  <rect width="1080" height="1920" fill="#efe6d2"/>
  <g id="world"><g id="ink" filter="url(#boil0)" stroke="#2b1d16" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
    <path d="M-60 1312 L770 1304 Q810 1306 812 1330 L800 2200 L-60 2200 Z" fill="#c9b79a"/>          
    <g id="hero"><g transform="scale(1.7)">                                                         
      <g id="body"><circle cx="0" cy="-100" r="92" fill="#7fb6c9"/><path d="M-92 -100 a92 92 0 0 0 184 0" fill="url(#hatch)" stroke="none"/>
        <rect x="-58" y="-128" width="116" height="62" rx="30" fill="#fbf6ea"/>
        <g id="eyes" stroke="none" fill="#2b1d16"><ellipse cx="-24" cy="-98" rx="11" ry="15"/><ellipse cx="24" cy="-98" rx="11" ry="15"/></g>
        <g id="armR"><path d="M88 -92 q40 10 52 46" fill="none" stroke-width="12"/></g></g>
    </g></g>
  </g></g>
  <rect width="1080" height="1920" filter="url(#grain)"/>
</svg></div>
<script>
const tl = gsap.timeline({ paused: true });
for (let i = 0; i < 90; i++) tl.set("#ink", { attr: { filter: `url(#boil${i % 3})` } }, i / 6);   // boil on twos
gsap.set("#hero", { x: 400, y: 1316 });                                                          // feet on the stage
tl.to("#body", { scaleY: 0.85, scaleX: 1.1, svgOrigin: "0 6", duration: 0.1, yoyo: true, repeat: 1 }, 1.7);  // anticipation
tl.to("#hero", { y: 956, duration: 0.45, ease: "power2.out" }, 1.95).to("#hero", { y: 1316, duration: 0.25, ease: "power3.in" }, 2.9);
tl.to("#body", { scaleY: 0.5, scaleX: 1.35, svgOrigin: "0 6", duration: 0.08 }, 3.15)
  .to("#body", { scaleY: 1, scaleX: 1, svgOrigin: "0 6", duration: 0.5, ease: "elastic.out(1,0.4)" }, 4.3);
tl.to("#armR", { rotation: -60, svgOrigin: "88 -92", duration: 0.16, yoyo: true, repeat: 5 }, 13.6);   // wave
tl.set({}, {}, 15);
window.__timelines["main"] = tl;
</script>

添加一个 <head>,其中包含 GSAP 脚本(https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js)、Google Fonts 链接,以及 html,body{margin:0;width:1080px;height:1920px;overflow:hidden}。将节拍列表中的动作依次扩展到同一个 tl 上。

需要公开的参数

format · duration · tone · characterColor · accent · paper · title · endLine · beats。将它们作为常量放在脚本顶部。

避免

  • 完美的几何形状和干净、没有抖动的线条(会显得像扁平矢量图)。
  • 在角色上使用渐变、辉光和投影。
  • 角色太小,淹没在空白中。角色高度应占画面高度的 25–45%。
  • 在角色脸部上叠加文字,也不要在 9:16 画面的底部 670 px 或顶部 270 px 区域放置文字。
  • 使用 Math.random() 或依赖一天中的时间。抖动效果和其他特效必须由时间轴驱动。

故障模式

表现原因修复方法
画面干净得像矢量图,不像手绘没有抖动,形状过于完美每隔 2 帧切换 3 个位移滤镜;使用手绘路径
缩放时角色漂移或下沉缩放动画加在移动组上将尺寸设置在内层 <g transform="scale()"> 上;使用 svgOrigin 以双脚为基准压缩
效果从角落开始效果出现前未设置 x/y在时间 0 使用 gsap.set 设置起始位置
画面浑浊、对比度低中间色调过多纸张色调明亮,墨色深沉,只用一种强调色
动作僵硬动作没有预备或收势先加入压缩动作,再加入弹性收势
堆叠标题出现文字重叠警告手写字体的行框相互接触行间增加 10–20 px;通过快照确认

验证

npx hyperframes check
npx hyperframes snapshot --at <one time per beat, plus one mid-transition>
npx hyperframes render -o renders/<name>.mp4

查看每一张快照和 snapshots/contact-sheet.jpg:

  • 间隔 1/6 秒的两张快照中,线条明显不同(抖动有效)
  • 角色站在舞台上(没有下沉),且在手机尺寸下清晰可辨
  • 每个节拍的剪影都能表达清楚;失败和结果呈现足够突出
  • 文字位于安全区域内;过渡过程中没有任何内容碰撞
  • 最后一帧与第一帧一致

最终渲染前的自我检查

根据以下方面给快照表打 1–10 分:吸引力(第一秒)、手机屏幕宽度下的可读性、动作质量、构图、准确性,以及“静音测试”(观众能否在没有声音的情况下用一句话概括内容?)。只修复得分为 7 分或以下的项目,然后重新生成快照并重复检查,直到所有项目都达到 8 分或以上。如果可以,请让另一个只读检查流程负责评分,这样就不会给自己的作品打分。

不要这样做应该这样做
在两个内容繁杂的布局之间交叉淡化先移除旧内容,再引入新内容;或者让一个物体贯穿其中
同时让多件事动起来错开动作,每个时刻只突出一个焦点
用漂浮或摇晃填充停顿保持静止,或让角色做出动作
为了营造“活力”而添加粒子、彩虹渐变或 3D 翻转加入更有力的事件:揭示、撞击或镜头移动
为了赶时间而压缩文字删减文字

交付

  • 选取最有力的定格画面作为海报:ffmpeg -ss <t> -i renders/<name>.mp4 -frames:v 1 poster.jpg。将其嵌入为第 0 帧,让缩略图显示该画面:ffmpeg -i renders/<name>.mp4 -i poster.jpg -filter_complex "[0:v][1:v]overlay=enable='eq(n,0)'" -c:a copy renders/<name>-final.mp4。
  • 撰写 1–2 句说明,点明创意,而不是技法。
  • 告诉用户每个节拍中发生了什么,以及修改时要调整哪个参数。

参考作品

评论

支持 Markdown