首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Day 3:我让洗衣机"活"了——CSS 动画 + Gemini AI 接入实录

Day 3:我让洗衣机"活"了——CSS 动画 + Gemini AI 接入实录

作者头像
袁锐钦
发布2026-04-16 16:16:52
发布2026-04-16 16:16:52
1150
举报

袁锐钦 · AI编程实


Day 2 搞定了认证和数据库,Picboil 已经是一个能注册、能登录、有数据存储的"半成品"了。

但它还不会动。

洗衣机静静地躺在页面上,像个摆设。用户输入提示词,什么都不会发生。因为没有东西去处理这个输入,没有动画反馈,更没有 AI 在后面接单干活。

Day 3 只干一件事:让这台洗衣机从"摆设"变成"活的机器"。

具体来说三件事:

  1. 1. CSS 动画完善 — 滚筒转动、泡泡冒出、灯光闪烁
  2. 2. Gemini AI 接入 — 真正能调用 AI 生成图片
  3. 3. 完整交互链路 — 从输入到出图,全流程跑通

第一步:让洗衣机动起来(纯 CSS 搞定)

Day 1 我已经用 CSS 做了一个卡通洗衣机的静态外观。今天要让它动起来。

滚筒转动

最核心的动画:滚筒要转。用 CSS @keyframes 就行:

代码语言:javascript
复制




@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.drum {
  animation: spin 2s linear infinite;
}

就这么简单?对,就这么简单。

但有个细节:滚筒转动时里面的内容不能跟着转。你想想真实洗衣机——滚筒在转,但衣服是翻滚着掉下来的,不是贴在筒壁上跟着转圈。

所以用了两层结构:外层转,内层内容用反向动画或者固定定位保持相对稳定。

💡 提示词模板: "创建一个 CSS 洗衣机滚筒动画,要求:①外层圆环持续匀速旋转 ②内部内容区域保持水平不跟随旋转 ③使用 overflow:hidden 裁剪超出部分 ④加入微弱的 3D 透视效果(perspective)增加立体感"

泡泡效果

洗衣机运转时要有泡泡从边缘冒出来。这也是纯 CSS:

代码语言:javascript
复制




@keyframes bubble-rise {
  0% {
    transform: translateY(0) scale(1);
    opacity: 0.8;
  }
  100% {
    transform: translateY(-80px) scale(0.3);
    opacity: 0;
  }
}

.bubble {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.6);
  animation: bubble-rise 2s ease-out infinite;
}

关键点:每个泡泡的 left 位置、animation-delaysize 都随机化,这样看起来自然不像复制粘贴。AI 帮我生成了 6-8 个泡泡元素,每个参数略有不同。

灯光变化

洗衣机运行时内部灯光要有变化——模拟"正在工作"的状态感:

代码语言:javascript
复制




@keyframes glow-pulse {
  0%, 100% { box-shadow: inset 0 0 20px rgba(255, 107, 53, 0.3); }
  50% { box-shadow: inset 0 0 40px rgba(78, 205, 196, 0.5); }
}

运行中灯光在橙色(主色)和蒸汽蓝(辅助色)之间呼吸切换,告诉用户"我在工作中"。

三个动画加在一起的效果: 滚筒匀速转 → 泡泡不断冒 → 灯光柔和闪。一台活生生的 CSS 洗衣机,零 JavaScript,性能好得离谱。

如果 Day 1 用的是 React Three Fiber 方案,光是加载 Three.js 就要 1MB+。现在整个动画代码不到 3KB。这就是不过度工程化的好处。


第二步:接上 AI 大脑(Gemini API)

动画有了,但洗衣机只是空转——扔进去的东西不会变成图片。需要接一个真正的 AI 图片生成模型。

为什么选 Gemini

方案

免费额度

价格

质量

接入难度

DALL-E 3

~$0.04/张

Midjourney API

~$0.05/张

Stable Diffusion

自建

电费+GPU

Gemini Imagen 3

$300 + 500张/天免费

~$0.01/张

MVP 阶段我的优先级很明确:成本低 > 接入简单 > 质量够用。Gemini 三条全中。

$300 的新用户额度意味着开发测试阶段几乎零成本。500 张/天的 Flash Image 免费额度甚至够小规模运营用。

接入过程

让 AI 帮我装 SDK:

代码语言:javascript
复制




npm install @google/generative-ai

然后写一个 API Route 作为后端代理:

代码语言:javascript
复制




// app/api/generate/route.ts
import { GoogleGenerativeAI } from '@google/generative-ai';

const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);

export async function POST(req: Request) {
  const { prompt, size } = await req.json();

  const model = genAI.getGenerativeModel({
    model: 'imagen-3.0-generate-001', // Imagen 3 高质量模型
  });

  const result = await model.generateImages({
    prompt: prompt,
    config: {
      numberOfImages: 1,
      aspectRatio: size === 'portrait' ? '9:16' : '16:9',
    },
  });

  const imageData = result.generatedImages[0].image.imageBytes;
  
  // 转 base64 返回给前端
  return Response.json({
    image: `data:image/png;base64,${imageData}`,
  });
}

💡 提示词模板: "用 Next.js App Router 创建一个 API Route(app/api/generate/route.ts),对接 Google Gemini Imagen 3 API 实现文生图功能。要求:①使用 @google/generative-ai SDK ②接收 prompt 和 size 参数 ③返回 base64 编码的图片 ④加入错误处理(API Key缺失、配额超限、内容过滤)⑤不要把 API Key 暴露到前端代码里"

为什么要用 API Route 做代理? 因为 API Key 绝对不能放到前端代码里。任何人打开浏览器 F12 就能看到你的 Key,然后拿去刷爆你的额度。所有涉及 API Key 的调用都必须走后端。

图生图也一起接了

Picboil 不只有文生图,还有图生图。Gemini 同样支持:

代码语言:javascript
复制




// 图生图:传入参考图片 + 提示词
const result = await model.generateImages({
  prompt: 'Transform this image into a cartoon style',
  config: {
    numberOfImages: 1,
    inputConfig: {
      imageData: referenceImageBytes, // 用户上传的图片
      mimeType: 'image/png',
    },
  },
});

一个 API,两种模式都搞定了。


第三步:完整交互链路跑通

这是今天最有成就感的部分——把前面做的所有东西串起来

完整流程是这样的:

链路拆解

① 输入阶段

用户在便签上输入提示词(比如 "a cute cat wearing a spacesuit"),点击 "Boil it" 按钮。

② 投递动画

便签纸卷起 → 飞向洗衣机投料口 → 洗衣机盖子关上。这段用 Framer Motion 做,大约 800ms 的动画时长。

③ "洗涤"动画

盖子关上后触发 CSS 动画序列:

  • • 滚筒开始转(spin keyframe)
  • • 泡泡开始冒(bubble-rise keyframe)
  • • 灯光开始闪(glow-pulse keyframe)
  • • 同时显示进度文字:"Washing... Rinsing... Spinning..."

这段动画故意设为 3-4 秒。为什么?因为如果 API 调用太快(有时候 Flash Image 1秒就返回),用户会觉得"这就完了?"——没有仪式感。稍微等一下,让用户觉得 AI 在认真"煮"他的图。

④ API 调用

动画播放到后半段时,前端悄悄发起 fetch('/api/generate') 请求。用户还在看泡泡,后端已经在调 Gemini 了。

⑤ 结果弹出

API 返回图片后:

  • • 洗衣机发"叮!"的视觉提示(一个小铃铛图标闪现)
  • • 盖子弹开(弹性动画,ease-out-back 缓动)
  • • 图片带掉落动画落在结果展示区
  • • 用户可以下载或再煮一次

整个链路一气呵成,从点击到看到结果大约 5-8 秒。

其中 3-4 秒是动画(仪式感),2-4 秒是真正的 AI 生成时间。用户感知到的不是"等待",而是一场完整的体验

这就是 Picboil 和其他 AI 图片工具最大的区别:别人是"输入→等待加载→出图",我们是"输入→一场小演出→出图"。等待本身也可以是产品的一部分。


第四步:生成历史功能

用户生成过图片之后,得有地方看。这个功能用 Prisma 查询就能搞定:

代码语言:javascript
复制




// 查询当前用户的生成历史
const history = await prisma.task.findMany({
  where: { userId: currentUser.id },
  orderBy: { createdAt: 'desc' },
  take: 20,
  include: {
    images: true, // 关联生成的图片
  },
});

Day 2 设计数据库的时候已经建好了 tasks 表和 images 表,这里直接用。

展示区放在页面下方,卡片式布局,每张图显示缩略图、提示词摘要、生成时间。点击可以放大查看。

这个功能没什么技术难点,但对用户体验很重要——用户回来能看到自己之前的作品,才会有"这是我的工具"的感觉,而不是一个一次性用完就走的网页。


第五步:今天的踩坑记录

坑 1:base64 图片太大,首屏加载慢

现象: 生成的历史图片用 base64 存储和展示,3-4 张之后页面明显卡顿。

原因: 一张 Imagen 3 生成的图片 base64 编码后大概 2-3MB。直接内嵌在 HTML 里或通过 API 返回,体积太大。

怎么修:

改用 Cloudflare R2 对象存储。图片生成后上传到 R2,只返回一个 URL 给前端。R2 的读取请求几乎免费,而且自带 CDN 加速

代码语言:javascript
复制




// 生成图片 → 上传 R2 → 返回 URL
const buffer = Buffer.from(imageData, 'base64');
await r2.put(`images/${taskId}.png`, buffer);
return { url: `https://r2.picboil.ai/images/${taskId}.png` };

MVP 阶段先用本地文件系统存着,上线前再迁移到 R2。先跑通逻辑,再优化性能。

💡 提示词技巧: 让 AI 做图片相关功能时,加上"图片必须使用对象存储(如 AWS S3 / Cloudflare R2),禁止使用 base64 内嵌或 localStorage 存储。返回图片 URL 而非图片数据。"

教训:MVP 先跑通,但要知道哪里以后要改。 base64 能用但不代表应该用在生产环境。

坑 2:Gemini 内容过滤误杀正常提示词

现象: 输入 "a stylish girl in coffee shop" 这种完全正常的提示词,偶尔被 Gemini 的安全过滤器拦截,返回错误。

原因: Google 的安全策略比较保守,某些词组合会触发误判。这不是 bug,是策略问题。

怎么修:

  1. 1. 前端友好提示:不要直接报错说"API 错误",而是"这张图的配方有点特别,换个说法试试~"
  2. 2. Prompt 预处理:在发给 Gemini 之前,让 AI 帮忙润色一下提示词,去掉可能触过滤器的敏感词
  3. 3. 备选方案:主模型被拦截时自动降级到 Flash Image 再试一次
代码语言:javascript
复制




// 简化的降级逻辑
async function generateWithFallback(prompt) {
  try {
    return await generateWithImagen3(prompt); // 主力模型
  } catch (error) {
    if (isSafetyFilterError(error)) {
      return await generateWithFlash(prompt); // 降级重试
    }
    throw error;
  }
}

教训:AI 模型的安全策略是不可控的外部因素。 你能做的是优雅地处理失败,而不是试图绕过它。好的错误体验 = 好的产品体验。

坑 3:动画和 API 时序配合问题

现象: 有时候 API 返回很快(< 2秒),但动画还没播完,图片就已经出来了——打断了动画节奏。或者反过来,API 很慢(> 10秒),动画播完了图片还没来,用户盯着静止的洗衣机发呆。

原因: 动画时长固定,API 耗时不固定,两者没协调好。

怎么修:

用一个状态机管理整个过程:

代码语言:javascript
复制




type MachineState = 'idle' | 'animating' | 'generating' | 'done' | 'error';

// 动画开始 → 同时发起 API 请求(不等动画结束)
// API 先回来 → 等动画播到合适节点再展示结果
// 动画先完 → 显示 "Almost ready..." 等待文字
const [state, setState] = useState<MachineState>('idle');

const handleGenerate = async () => {
  setState('animating'); // 开始动画
  const apiPromise = fetch('/api/generate'); // 同时请求

  // 等动画播到 70% 左右
  await minDelay(apiPromise, 3000);
  
  setState('done'); // 展示结果
};

核心思路:动画和 API 并行跑,谁慢等谁,但至少保证 3 秒的最短体验时间。

💡 提示词模板: "实现一个异步状态机,管理'点击→动画→API调用→结果展示'的完整流程。要求:①动画和 API 并行执行 ②设置最小体验时长(如 3秒)③API 快于动画时等动画播到 80% 再展示 ④API 慢于动画时显示过渡态 ⑤处理好取消和重复点击的场景"


Day 3 总结

今天完成了什么

✅ CSS 洗衣机动画完善(滚筒转动 + 泡泡效果 + 灯光脉冲) ✅ Gemini Imagen 3 API 接入(文生图 + 图生图) ✅ 完整交互链路跑通(输入 → 投递 → 洗涤动画 → AI 生成 → 结果弹出) ✅ 生成历史功能(Prisma 查询 + 卡片式展示) ✅ 对象存储方案确定(Cloudflare R2,MVP 阶段先本地) ✅ 安全过滤降级策略(主模型拦截自动降级 Flash)

今天的花费

项目

花费

时间

约 6 小时

金钱

$0(Gemini 免费额度)

新增依赖

@google/generative-ai

修复 Bug 数

3 个

新增动画

3 组(spin / bubble / glow)

一个重要的体会

Day 1 教会我"不要过度工程化",Day 2 教会我"AI 写的代码不能直接信",Day 3 教会我的是——

产品的灵魂不在技术栈有多牛,而在"感受"的设计上。

同样的 Gemini API,别人做成一个输入框+加载圈+出图,我做成了"便签飞进去→洗衣机转起来→泡泡冒出来→图片跳出来"。

技术上没有任何高深的地方。CSS 动画是基础语法,API 调用是标准 CRUD。但用户感受到的东西完全不同。

做出海站、做产品,功能可以被复制(你也接 Gemini 我也接 Gemini),但这种"用起来的感觉"很难被复制。

这也是我为什么花那么多时间在动画和交互链路上——因为这才是用户记住你的原因。


当前进度(截至写稿时)

三天下来,Picboil 已经是一个能完整走通"注册→登录→输入→生成→查看历史"流程的最小可用产品了:

✅ 项目骨架(Next.js 15 + TypeScript + Tailwind) ✅ CSS 卡通洗衣机(替代原 3D 方案) ✅ 认证系统(邮箱 + Google OAuth) ✅ 数据库(4 张表 + 软删除) ✅ AI 图片生成(Gemini Imagen 3 + Flash 降级) ✅ 完整交互链路(动画 + API + 状态机) ✅ 生成历史

🔲 下一步 Day 4:

  • • 首页 Hero 区搭建(导航栏 + 大 CTA + 特性卡片)
  • • 定价页面(Freemium 三档定价)
  • • 响应式适配(手机端体验优化)

三天完成了 MVP 的核心功能,接下来就是包装和上线了。


如果你在做 AI 辅助开发,或者对这些踩坑经历有共鸣,欢迎交流。

一个人走得快,一群人走得远。💪

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Ruiqin袁锐钦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一步:让洗衣机动起来(纯 CSS 搞定)
    • 滚筒转动
    • 泡泡效果
    • 灯光变化
  • 第二步:接上 AI 大脑(Gemini API)
    • 为什么选 Gemini
    • 接入过程
    • 图生图也一起接了
  • 第三步:完整交互链路跑通
    • 链路拆解
  • 第四步:生成历史功能
  • 第五步:今天的踩坑记录
    • 坑 1:base64 图片太大,首屏加载慢
    • 坑 2:Gemini 内容过滤误杀正常提示词
    • 坑 3:动画和 API 时序配合问题
  • Day 3 总结
    • 今天完成了什么
    • 今天的花费
    • 一个重要的体会
  • 当前进度(截至写稿时)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档