📄 文档管理系统

← 返回列表

用 HyperFrames + GPT-image-2 制作母亲节视频:技术人的浪漫表达

技术文章 #HyperFrames,母亲节,GPT-image-2,文生图,视频制作,技术人的浪漫 📅 创建:2026-05-10 01:12:05 🔄 更新:2026-05-10 01:14:02
👁️ 预览 & 复制到公众号 ✏️ 编辑

051001.png# 用 HyperFrames + GPT-image-2 制作母亲节视频:技术人的浪漫表达

昨天晚上 11 点多,突然想起来今天是母亲节,想做点什么。

本来已经准备睡了,但脑子里转了一下——用代码传递情感,用技术表达温度,这事儿好像挺有意思的。

一开始尝试用即梦的 Seedance 2.0 和 Coze 分别做了视频,但效果都不理想——要么动作僵硬,要么风格不稳定,反复调了几版都没达到预期。

后来静下心来想,既然要表达情感,画面质量必须过关。想到了最近一直高频使用的 HyperFrames,再配上最近效果最好的 GPT-image-2 文生图,思路一下子清晰了。

整理了一下大概的方向,折腾了差不多半小时,终于把这个视频做出来了。趁记忆新鲜,把制作过程记录下来,供有同样想法的朋友参考。


为什么用 GPT-image-2

之前用过几个文生图工具,Stable Diffusion、Midjourney、DALL-E,各有特点。但这次有个特殊需求:风格统一

母亲节视频不是一张图,是一组图——开场、配图、结尾,每张都要有统一的视觉风格。以前用 Midjourney 的时候,靠 seed 控制,但用起来总是不太稳定,同一个 prompt 跑出来的图色调、构图经常有差异。

这次试了 OpenAI 的 GPT-image-2(ChatGPT 4o 的图像生成能力),效果出乎意料。关键优势:

1. 风格一致性极强
同一个对话里连续生成多张图,角色一致性很高。面孔、配色、构图风格基本一致,不会有"这是同一个人吗"的问题。

2. 自然语言控制
直接用自然语言描述你要什么,不用记一堆参数。中文理解也很准确。

3. 响应速度快
比大多数云端图生图服务快,生成一张图通常 10-20 秒。


图片生成过程

第一步:定义视觉风格

在开始生成之前,先想清楚整个视频的视觉调性。

母亲节视频,我定义的风格是:温暖、柔和、真诚。不用太炫酷的特效,色调偏暖黄和粉色系,构图简洁,主体突出。

每个 prompt 前面都加了一句统一的风格描述:

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。

后续的每一张图,都在这个基础上描述具体内容。

第二步:批量生成系列图

确定风格后,按视频顺序逐张生成。以下是我实际用过的 10 个 prompt:


【开场海报】对应旁白:"他们说,我是你九死一生换来的宝贝。可你看向我的那一刻,却笑着像得到了全世界。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。生成一张海报,内容如下:我曾是你九死一生换来的宝贝,也是你往后余生唯一的软肋。你拉着我的手学会走路,把伞歪向我这边淋湿了肩,你陪我熬过无数个刷题的深夜,目送我一次次离开,又笑着等我回来。妈,今天我不想当大人。母亲节快乐,我最勇敢的超人 ❤️——谨以此片,献给全天下每一位妈妈。


【场景1】对应旁白:"1岁时,我跌跌撞撞走向你,你张开的手臂,是我人生第一个确信的终点。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。幼儿仰视视角,年轻的妈妈蹲在前方张开双臂,微笑鼓励,逆光形成金色轮廓光,午后温暖阳光,木地板上有人影,柔软治愈,高细节。


【场景2】对应旁白:"后来,每一个雨天,伞总是歪向我这边。你淋湿的肩膀,成了我晴天的屋檐。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。孩子透过雨水的视角,校门口,妈妈撑着透明雨伞蹲下迎接,伞倾向镜头,母亲肩膀淋湿,水洼倒映暖黄路灯,治愈雨景,绘本风格,高细节。


【场景3】对应旁白:"那些熬夜刷题的晚上,你什么都不说,只是一次次热好牛奶,陪我到很晚很晚。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。第一人称视角写作业,台灯暖光,一只手轻轻放下一杯热牛奶,牛奶冒热气,桌上试卷和书本,背景昏暗有便利贴,温暖静谧,高画质。


【场景4】对应旁白:"离开家的那天,你笑着挥手,我假装没看见,你转身时擦掉的眼角。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。透过火车车窗视角,站台上妈妈挥手告别,笑容带泪,隐约白发,晨光逆光,微颗粒感,电影感,温暖又感伤,高细节。


【场景5】对应旁白:"大学里,每次挂掉电话,我都在想,你肯定又对着屏幕唠叨了很久,哪怕我早已按下了静音键。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。第一人称手持手机视频通话,屏幕里妈妈笑容温暖,背景是家中沙发,屏幕外宿舍床沿,连接光束,现代温情,高画质。


【场景6】对应旁白:"工作后每次回家,开门的瞬间,你眼里的光,让我知道,我永远是被期待的。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。推门入户的第一人称视角,妈妈系着围裙惊喜迎来,手上沾面粉,厨房热气,暖黄灯光,温馨扑面,高画质。


【场景7】对应旁白:"今天,我不想长大,只想赖在你怀里,再当一回小孩。妈,母亲节快乐,我最勇敢、最温柔的超人。"

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。温暖治愈绘本风格,暖黄午后阳光,成年孩子与头发花白的妈妈幸福拥抱,妈妈的脸轻靠在孩子肩头,眼带泪光微笑,孩子双手环抱着母亲,背景是温馨的客厅或窗边,画面充满爱与感恩,高画质,右上适当留白。


【场景8】无旁白,静默展示

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。9:16比例,统一温暖治愈绘本风,婴儿主观视角,镜头微微模糊,画面中央是一位25岁年轻亚洲妈妈温柔的笑脸,黑色长发垂落,眼神充满爱意,背景是柔和的产房灯光,暖奶油色调,梦幻柔焦,治愈系绘本风格,高画质。


【结尾图】无旁白,静默展示

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。画面中央是一束盛开的粉色康乃馨,柔和的金色晨光从侧面洒落,花瓣上带着晶莹露珠。背景虚化处理,可见朦胧的暖黄色光斑,如家的灯光。花朵下方,优雅的白色手写体中文浮现:"祝全天下母亲节日快乐,您辛苦了!在我心中您永远年轻!"。整体色调温暖治愈,电影感光影,高画质。


关键技巧:每个 prompt 前面都统一加上风格描述(9:16竖屏构图、温馨的母亲节主题插画、温暖治愈色调、电影感光影),保证全系列风格一致。后续图片在已生成的图片基础上微调,比如"与之前生成的图X风格一致"。


视频内容规划

视频结构

┌─────────────────────────┐
│     开场海报(4秒)     │
│   诗歌文案 + 标题       │
└─────────────────────────┘
┌─────────────────────────┐
│     场景1(幼儿仰视妈妈)│
│     场景2(雨伞接孩子)  │
│     场景3(深夜送牛奶)  │
│     场景4(车站送别)    │
│     场景5(视频通话)    │
│     场景6(推门回家)    │
│     场景7(母子拥抱)    │
│     场景8(妈妈笑脸)    │
└─────────────────────────┘
┌─────────────────────────┐
│     结尾康乃馨(3秒)   │
│   静默 + "母亲节快乐"   │
└─────────────────────────┘

总时长约 90 秒。


核心代码实现

项目结构

mothers-day/
├── index.html              # 主 composition
├── music.mp3               # 背景音乐
├── pic/                    # 图片素材
│   ├── poster.png          # 开场海报
│   ├── scene1.png          # 幼儿仰视妈妈
│   ├── scene2.png          # 雨伞接孩子
│   ├── scene3.png          # 深夜送牛奶
│   ├── scene4.png          # 车站送别
│   ├── scene5.png          # 视频通话
│   ├── scene6.png          # 推门回家
│   ├── scene7.png          # 母子拥抱
│   ├── scene8.png          # 妈妈笑脸
│   └── ending.png          # 结尾康乃馨
└── content.md              # 文案存档

HTML 核心代码

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=1080, height=1920" />
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      html, body {
        margin: 0;
        width: 1080px;
        height: 1920px;
        overflow: hidden;
        background: linear-gradient(135deg, #FFF5F7 0%, #FFE4EC 50%, #FFF0F5 100%);
        font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
      }

      .scene-content {
        display: flex;
        flex-direction: column;
        width: 100%;
        height: 100%;
      }

      .poster-area,
      .scene-area,
      .ending-area {
        position: absolute;
        top: 0;
        left: 0;
        width: 1080px;
        height: 1920px;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 80px 60px;
      }

      .scene-img {
        max-width: 90%;
        max-height: 85%;
        border-radius: 20px;
        box-shadow: 0 20px 60px rgba(0,0,0,0.12);
        position: absolute;
        opacity: 0;
        object-fit: cover;
      }

      .poster-text {
        position: absolute;
        bottom: 120px;
        font-size: 36px;
        line-height: 1.8;
        color: #FFFFFF;
        text-align: center;
        padding: 40px;
        background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
        width: 100%;
        left: 0;
        right: 0;
      }

      .subtitle-area {
        position: absolute;
        bottom: 260px;
        left: 0;
        right: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 16px;
      }

      .subtitle {
        font-size: 42px;
        font-weight: 600;
        color: #FFFFFF;
        text-align: center;
        padding: 20px 36px;
        background: rgba(233, 30, 99, 0.88);
        border-radius: 14px;
        opacity: 0;
        text-shadow: 0 2px 8px rgba(0,0,0,0.3);
        box-shadow: 0 8px 32px rgba(233, 30, 99, 0.35);
        max-width: 88%;
        line-height: 1.6;
      }

      .ending-text {
        position: absolute;
        bottom: 160px;
        font-size: 48px;
        font-weight: 700;
        color: #FFFFFF;
        text-align: center;
        padding: 28px 48px;
        background: rgba(233, 30, 99, 0.9);
        border-radius: 16px;
        text-shadow: 0 2px 8px rgba(0,0,0,0.3);
      }
    </style>
  </head>
  <body>
    <div id="root" data-composition-id="main" data-start="0" data-duration="32" data-width="1080" data-height="1920">
      <div class="scene-content">

        <!-- 开场海报 -->
        <div class="poster-area" id="poster">
          <img class="scene-img" id="poster-img" src="./pic/poster.png" alt="开场" />
          <div class="poster-text">妈妈,我爱你 ❤️</div>
        </div>

        <!-- 场景轮播 -->
        <div class="scene-area" id="scenes">
          <img class="scene-img" id="s1" src="./pic/scene1.png" alt="幼儿仰视妈妈" />
          <img class="scene-img" id="s2" src="./pic/scene2.png" alt="雨伞接孩子" />
          <img class="scene-img" id="s3" src="./pic/scene3.png" alt="深夜送牛奶" />
          <img class="scene-img" id="s4" src="./pic/scene4.png" alt="车站送别" />
          <img class="scene-img" id="s5" src="./pic/scene5.png" alt="视频通话" />
          <img class="scene-img" id="s6" src="./pic/scene6.png" alt="推门回家" />
          <img class="scene-img" id="s7" src="./pic/scene7.png" alt="母子拥抱" />
          <img class="scene-img" id="s8" src="./pic/scene8.png" alt="妈妈笑脸" />
        </div>

        <!-- 字幕 -->
        <div class="subtitle-area">
          <div class="subtitle clip" data-start="4" data-duration="3.2" data-track-index="10">他们说,我是你九死一生换来的宝贝。<br/>可你看向我的那一刻,<br/>却笑着像得到了全世界。</div>
          <div class="subtitle clip" data-start="7.5" data-duration="3.2" data-track-index="11">1岁时,我跌跌撞撞走向你,<br/>你张开的手臂,<br/>是我人生第一个确信的终点。</div>
          <div class="subtitle clip" data-start="11" data-duration="3.2" data-track-index="12">后来,每一个雨天,<br/>伞总是歪向我这边。<br/>你淋湿的肩膀,成了我晴天的屋檐。</div>
          <div class="subtitle clip" data-start="14.5" data-duration="3.2" data-track-index="13">那些熬夜刷题的晚上,你什么都不说,<br/>只是一次次热好牛奶,<br/>陪我到很晚很晚。</div>
          <div class="subtitle clip" data-start="18" data-duration="3.2" data-track-index="14">离开家的那天,你笑着挥手,<br/>我假装没看见,<br/>你转身时擦掉的眼角。</div>
          <div class="subtitle clip" data-start="21.5" data-duration="3.2" data-track-index="15">大学里,每次挂掉电话,<br/>我都在想,你肯定又对着屏幕<br/>唠叨了很久。</div>
          <div class="subtitle clip" data-start="25" data-duration="3.2" data-track-index="16">工作后每次回家,<br/>开门的瞬间,你眼里的光,<br/>让我知道,我永远是被期待的。</div>
          <div class="subtitle clip" data-start="28.5" data-duration="3.2" data-track-index="17">今天,我不想长大,<br/>只想赖在你怀里,再当一回小孩。<br/>妈,母亲节快乐!</div>
        </div>

        <!-- 结尾 -->
        <div class="ending-area" id="ending">
          <img class="scene-img" id="ending-img" src="./pic/ending.png" alt="结尾" />
          <div class="ending-text">祝全天下母亲节日快乐 ❤️</div>
        </div>

      </div>
    </div>

    <!-- 背景音乐 -->
    <audio id="bgm" data-start="0" data-duration="32" data-track-index="99" src="./music.mp3" data-volume="0.2"></audio>

    <script>
      window.__timelines = window.__timelines || {};
      const tl = gsap.timeline({ paused: true });
      const totalDuration = 32;

      // 海报入场
      tl.fromTo("#poster-img",
        { opacity: 0, scale: 0.9 },
        { opacity: 1, scale: 1, duration: 1.5, ease: "power3.out" },
        0.5);
      tl.to("#poster", { opacity: 0, duration: 0.8 }, 3.5);

      // 场景轮播(每张图展示3秒,间隔0.3秒过渡)
      const sceneIds = ["#s1","#s2","#s3","#s4","#s5","#s6","#s7","#s8"];
      sceneIds.forEach((id, i) => {
        const start = 4.3 + i * 3;
        tl.fromTo(id,
          { opacity: 0, scale: 0.95 },
          { opacity: 1, scale: 1, duration: 0.5, ease: "power2.out" },
          start);
        if (i < sceneIds.length - 1) {
          tl.to(id,
            { opacity: 0, duration: 0.4, ease: "power2.in" },
            start + 2.6);
        }
      });

      // 字幕动画
      const subs = [
        { s: 4, id: "#subtitle1" },
        { s: 7.5, id: "#subtitle2" },
        { s: 11, id: "#subtitle3" },
        { s: 14.5, id: "#subtitle4" },
        { s: 18, id: "#subtitle5" },
        { s: 21.5, id: "#subtitle6" },
        { s: 25, id: "#subtitle7" },
        { s: 28.5, id: "#subtitle8" }
      ];
      subs.forEach(sub => {
        tl.fromTo(sub.id,
          { opacity: 0, y: 20 },
          { opacity: 1, y: 0, duration: 0.4 },
          sub.s);
        tl.to(sub.id,
          { opacity: 0, duration: 0.35 },
          sub.s + 2.85);
      });

      // 结尾入场
      tl.fromTo("#ending-img",
        { opacity: 0, scale: 0.9 },
        { opacity: 1, scale: 1, duration: 1.2 },
        28.5);

      // 背景音乐
      tl.call(() => {
        const bgm = document.querySelector("#bgm");
        if (bgm) { bgm.volume = 0.2; bgm.play(); }
      }, [], 0);

      window.__timelines["main"] = tl;
    </script>
  </body>
</html>

关键实现点

1. 统一风格前缀
每个 prompt 前面都加相同的风格描述,保证 10 张图风格高度统一:

9:16竖屏构图,温馨的母亲节主题插画。整体色调温暖治愈,电影感光影,高画质。

2. 海报开场
开场用一张诗歌文案海报,配上"妈妈,我爱你 ❤️"的主标题,视觉冲击力强。

3. 场景平滑过渡
每张图展示约 2.6 秒,淡入淡出间隔 0.4 秒,过渡自然不跳切。

4. 旁白字幕较长,用 <br/> 换行
每条旁白文案较长,在 HTML 里用 <br/> 手动换行,控制每行长度,保证阅读体验。

5. 背景音乐
音量固定 0.2,烘托气氛不抢戏。


渲染与输出

cd mothers-day

# 检查语法
npx hyperframes lint .

# 预览
npx hyperframes preview --port 3002

# 渲染
npx hyperframes render --duration 32 --output output.mp4 --quality standard

踩坑记录

坑1:图生图的风格不一致
- 原因:分开多次生成,每次都像抽卡
- 解决:每个 prompt 前面统一加风格前缀(9:16竖屏构图、温暖治愈色调、电影感光影),保证全系列一致

坑2:字幕太长换行乱
- 原因:字幕 text 直接放进去,长句子不会自动换行
- 解决:用 <br/> 手动换行,控制每行长度在 15 字以内

坑3:背景色和图片冲突
- 原因:有些图的背景偏白,和渐变背景融在一起
- 解决:CSS 渐变背景用偏暖的粉色系,和大多数图都能搭配;图片加 box-shadow 产生立体感


写在最后

今天母亲节,用不到两小时做了这个视频,技术上没什么难度,但做的时候心里挺暖的。

程序员这个群体很多时候被贴上"不懂浪漫"的标签,但其实浪漫不浪漫,跟职业无关,跟有没有心有关。

用代码传递情感,用技术表达温度,这大概是我能想到的最 geek、也最真诚的方式了。

最后,祝全天下所有的母亲——节日快乐!


你在母亲节做了什么有意思的事?有没有用技术表达情感的经历?欢迎评论区分享。

💬 评论区

加载中...