文本转 GIF 动画 — React Pet 项目开发日志

2025-06-07

文本转 GIF 动画 — React Pet 项目开发日志

我有一个想法,用简单的文本创建故事板会很酷:

具有两个框架的概念

用户描述场景并获得图像作为输出

在本文中,我将与大家分享我对这个项目的想法和构建过程。

我一定会向你展示最终结果!

剧透:事情没有按计划结束。tl ;dr 最终结果

0. 想法

我开始将这个想法视觉化。首先用纸笔,然后是draw.io(你可以在上面看到第一个概念之一),最后写一个概括性的概述:

用户使用简单的英语定义场景:

Scene
Ann as woman to the right
Frame
Ann says "Hello!"

场景定义描述演员(装饰)和框架
演员使用预设的精灵,如,,tree框架定义演员执行的动作manwoman

用户将能够分享他们的故事并“喜欢”和修改其他人的故事。

当想法和语言或多或少确定下来后,就该起草开发计划了:

  1. 设置——项目准备
  2. JSON 到图像测试我是否可以按我想要的方式创建图像
  3. 图像转为 GIF确保我可以在客户端生成 GIF,尝试一些库
  4. 文本转 JSON我需要为该语言创建一个解析器
  5. 后端——需要登录/保存/共享流程
  6. 发布

注意:为了简洁起见,我不会提及很多死胡同的想法或愚蠢的错误:所以如果你觉得一切进展太顺利——那只是因为编辑的原因。此外,我会删减大量代码,并使用类似伪代码的风格来缩短源代码。如果你有任何问题——请随时提问!

我们走吧!

1. 设置

我需要一个 git 仓库来组织我的开发流程,并需要一个框架来加速开发。我使用了create-react-appgit init

npx create-react-app my-app
cd my-app
# [skipped TypeScript adding process]
git init -y
git commit -m "initial"
npm start

重要提示:我们需要快速测试我们的想法!使用哪种语言、框架或版本控制系统并不重要,只要你熟练掌握并高效使用即可。

2. JSON 转图像

我首先定义一个简单的 JSON 来测试我是否可以根据这个结构渲染图像。

JSON 应该描述:

  • sprites— 演员和装饰的图片 URL
  • scenes— 应包含并定位演员和装饰
  • 并且frames- 应该包含动作,例如“安向左移动”
({
  sprites: { name: 'http://sprite.url' },
  scenes:
    // scene descriptions
    { scene_ONE:
        { entries:
            /* entries with their sprites and position */
            { Ann: { sprite: 'woman'
                   , position: { /* ... */ }
                   }
            }
        },
    },
  frames:
    [ { scene_name: 'scene_ONE'
      , actions: [
          { target: 'Ann'
          , action: 'move'
          , value: {x, y}
          }
        ]
      }
    , // ...other frames
    ]
})

对于演员,我定义了三个预设精灵:treewomanman,并将相关图像添加到项目中。

-

现在我们将对每一帧执行所有动作(移动和说话)

// for each frame
const computedFrames = frames.map(frame => {
  // clone entries
  const entries = _.merge({}, frame.scene.entries);
  // perform actions on the target entry
  frame.actions.forEach(action => {
    const entry = entries[action.target];

    if (action.type == 'talk') {
      entry.says = action.value;
    }

    if (action.type == 'move') {
      entry.position = action.value;
    }
  });

  return { entries };
});

-

对于绘制入口精灵,我们肯定会使用canvas

// draw the entries
const images = computedFrames.map(frame => {
  const canvas = document.create('canvas');
  const ctx = canvas.getContext('2d');
  frame.entries.forEach(entry => {
    ctx.drawImage(entry.sprite); // for sprites
    ctx.fillText(entry.says);    // for speech
  });
  // return rendered frame URL
  return URL.createObjectURL(canvas.toBlob());
})

Canvas 可以将其内容导出为dataURLblob — 我们稍后需要它来生成 .gif!

^ 实际上,代码更加异步:toBlob是异步的,并且所有图像都应该在之前下载ctx.drawImage,我使用了Promise链来处理这个问题。

至此,我已经证明图像可以按预期呈现:

第一个结果

第一个渲染图!太棒了!🙌

所以我们可以继续:

3. 图像转GIF

这需要研究一些可用的库。我最终选择了gif.js。可惜的是,它已经一年没更新了,不过它的工作做得还不错(demo)。

要生成 .gif 文件 — — 我们需要将每个图像提供给gif.js生成器,然后调用render()它:

const gif = new GIF({ /* GIF settings */ });

images.forEach(imgUrl => {
  const img = new Image();
  img.src = imgUrl;
  gif.addFrame(img, { delay: 1000 });
});

gif.on('finished', blob => {
  // Display the blob
  updateGifURL(URL.createObjectURL(blob));
});

gif.render();

太棒了,现在我们可以生成并下载 .gif 了:

gif.js生成

并采取行动!

4. 文本转 JSON

我希望用户能够用简单的英语输入命令。这对我来说是最难的部分,因为我甚至不知道从哪里开始:

  • 创建我自己的解析器?
  • input.split(/\n/)然后使用正则表达式?
  • 使用一些英语语法解析器?

幸运的是,经过一番搜索后,我偶然发现了这篇文章“使用 PegJS 编写 DSL 解析器”,它向我介绍了 PEG.js(@barryosull,谢谢)。

PEG.js是一个简单易用的解析器构建器:

  1. 使用类似正则表达式的规则来定义你的语言
  2. 它会.js使用您个人的全新解析器生成一个文件
  3. 你插入这个解析器并针对你的文本运行它

例如,下面是我的规则中解析 s 的摘录Scene

PEGjs摘录

这些规则将解析此文本:

Scene
with Tree as tree at { y: 160 scale: 1.5 }

对此 JSON:

{
    "type": "scene",
    "values": [
      {
        "type": "object",
        "objectName": "Tree",
        "sprite": "tree",
        "state": {
            "y": 160,
            "scale": 1.5
        }
      }
    ]
}

在使用 PEG.js在线版本几个小时后,我最终找到了一种适合使用的语言和输出结构。

将其插入应用程序后,我得到了这个:

PEGjs解析截图

看,他们说话和倾听

附注 1:此时,我放弃了模糊定位的想法Ann to the right,并更新了 PEG.js 来定义类似 js 的对象符号:Ann at { x: 100 y: 100 }

附注2:另外,我没法每次更新文本都重新生成GIF。这太麻烦了。每次按键都会阻塞100毫秒的UI线程,太麻烦了。

RxJS 😍 来救援了!用于输入文本更新的去抖动和一个简单的计时器,映射到帧切换imgRef.current.src = next_frame模拟动画。

只有当用户点击“下载”按钮时才会生成实际的 .gif!

5. 后端

这个宠物项目已经花费了我一个周末的开发时间,所以我不得不放弃所有与后端相关的任务,暂时坚持使用静态网络应用程序。

6.出版

我使用GitHub Pages功能来部署和共享项目。

GitHub Pages 将在其域名下为你的网站提供服务http://username.github.io/repository。由于我稍后可能会添加后端,所以我需要购买一个域名,以便我现在分享的所有链接将来仍然有效。

对我来说,取名字总是很难。经过一个小时的折腾,我最终确定了:

结果

去尝试一下framd.cc吧!🙂

结尾

剧情反转:部署项目并与朋友们分享后,我发现我的精灵图不够用了!原来,光靠男人、女人和一棵树的图片根本讲不完故事。所以我决定用表情符号做精灵图👻。现在,你拥有了海量表情符号🌳👩‍🚀🌍,可以好好讲述你的故事了

结束

就到这里!感谢阅读!🙏

有任何问题吗?请在评论区留言,我很乐意解答!

如果你喜欢阅读 — — 请考虑给这篇文章和这条推文点个赞

这很有帮助!

谢谢!

PS:一些 gif 示例:

心 孵化 什么 猴子 六人组

文章来源:https://dev.to/kosich/text-to-gif-animation-react-pet-project-devlog-5eel
PREV
书籍与在线课程
NEXT
Git 重置 --Hard git reset --explain