Claude3.5 打造漂亮的动态竞速图工具
时不时在网上看到一些漂亮的竞速图页面,展示数据随时间的变化情况,看着很酷。然后查了下有现成的 js 库可以生成这种图表,不过没找到一个合适的工具,可以将数据上传后,自动生成竞速图。
于是想着用 Claude3.5 实现一个,不过这个过程并不顺利,中间踩了不少坑。从 7 月份都开始着手,中间断断续续尝试了多次,直到最近才算实现了一个简单的版本。本文记录下用 Calude3.5 实现这个工具的过程,记录其中踩过的坑。最终的效果可以在这里体验,欢迎大家留言讨论。
失败尝试
最开始的时候,想着比较简单,就先去找了下目前哪些库支持动态竞速图。
决定用 于是就直接提示:
帮我用 react 实现一个网页工具,可以根据用户传的 json 数据,生成动态竞速表。
生成了一个粗糙的代码,需要特定的数据才能跑起来。并且效果也很差,就暂时搁置了。
继续尝试
最近用 cursor 比较舒服,就想着再来试试。原来的代码能解析 json,然后也能跑起来,这次想着一次性支持多点,能够支持传入 excel 或者 csv 文件,然后数据格式也可以自定义这样子。
打开原来的代码,选定代码后,直接提示:
我想支持传入 excel 或者 csv 文件,然后生成竞速直方图。 用 ReactECharts 实现,完善下面的代码。
然后 claude3.5 让我导入 excel 库,这里踩了坑,先让我从 xlsx 导入,结果安装的是 xlsx-js-style,货不对版。重新提示修好这些小问题后,让它生成了一份示例数据,然后想着来拿示例数据测试下。示例数据虽然短,不过内容倒是不错:
年份 | 类别 | 数值 |
---|---|---|
2015 | 苹果 | 100 |
2015 | 香蕉 | 80 |
2015 | 橙子 | 60 |
2015 | 葡萄 | 40 |
2015 | 芒果 | 20 |
2016 | 苹果 | 120 |
2016 | 香蕉 | 90 |
2016 | 橙子 | 70 |
2016 | 葡萄 | 50 |
2016 | 芒果 | 30 |
2017 | 苹果 | 130 |
2017 | 香蕉 | 110 |
2017 | 橙子 | 90 |
2017 | 葡萄 | 70 |
2017 | 芒果 | 50 |
这里第一列是时间,第二列是类别,第三列是值,每列名字可以自定义,传入文件后希望能根据时间,生成不同类别的数值动态竞速图。搞好一个版本后,发现传入csv 和 excel 还是不行,必须先传入 excel,再传一个 json 才能生成一个简单竞速图。先不说这个过程有多奇怪,这里竞速图的纵坐标也没有类别的名字。
为了让它能在纵坐标支持名字,花了好长时间,提示了几次也不太行。中间改动还导致连动态图也出不来了,然后加了日志,发现导入的数据解析有问题。让 Claude3.5 反复分析了好几轮,始终没有找到解决方法,看来 Claude3.5 还是不太适合做这种复杂的分析。
动态竞速图样例
算了,不再继续尝试,不如看看官方示例是怎么做的。毕竟只有自己懂怎么写,才能指导 AI 写出来。官方示例代码足够简单,样式也很好看,如下:
代码看起来也不是很复杂,导入数据后,指定下动态竞速图的配置,然后很简单就可以生成一个动态竞速图。把这部分提供给 Claude3.5 解读,让它作为参考代码,它肯定能理解。
既然生成动态竞速图的逻辑已经知道,那么剩下的就是处理前端页面,让用户可以上传文件,然后解析文件,生成动态竞速图。这里让 AI 直接生成这么多,还是有复杂度,人工拆分任务,让 Claude3.5 一点点完成呢?
拆分问题!
这里的任务其实挺好拆分的,先是上传和解析 Json 文件相关部分,接着可以加一个文件预览,这样方面使用者理解数据。有数据的情况下,再结合前面的官方示例代码,就可以生成动态竞速图了。这些都搞定后,可以在一个能跑的版本上慢慢优化迭代,比如增加导出 Gif 功能,或者支持不同颜色,支持添加标题等。后面如果想支持 Excel 或者 csv 也比较简单,只用写处理文件部分就好,其他都能复用。
文件上传、预览
这里先看第一部分,直接让 AI 生成一个上传组件,可以传 json 文件。同时为了不限制死文件格式,考虑让使用者选择某列来作为图表的数据源。其实这里需要的数据主要有 3 列,时间、类别和值。再传入 Json 文件后,直接解析里面的列名,然后返回选择框就行。
直接描述有点费劲,为了让 Claude3.5 更好理解,我直接给它画了个草图,如下:
让它参考这个实现。不过后来想了下,还是让页面整体布局和站点其他的保持一直,于是把设置相关的放到最右边去。Claude 实现的版本并不理想,有一些小问题,不过可以接着微调提示:
不对,你这里没有读上传的 json 文件啊。 要读文件内容,判断是否合法的 json,如果是合法的 json 则输出预览。 否则输出一个弹窗,告诉 json 解析错误。
这里的弹窗新建一个基础组建,有关闭按钮,在屏幕中央输出,上面有遮罩
这次添加了一个 Modal 遮罩组件,并且不止文件解析错误会输出。在没有选择数据源的时候,也会输出一个弹窗,这就是 Claude 带来的意外惊喜了。预览部分上面提示词没有说很详细,Claude 用一个表格来实现,有点小问题,不过很容易就能改对了。
竞速图生成部分
上传部分搞定了,也能预览数据了,接着就是生成竞速图了。提示词也很简单:
继续完善这里的代码,现在选择好列之后,点击生成图形,就要根据传入的 json,生成竞速图。
竞速图在预览的下方部分。
竞速图可以参考这里的: const updateFrequency = 2000; ...
这里从官方复制了完整的竞速图代码,上面不列出了。这次 Claude3.5 生成的代码很不错,终于能复现官网的例子了。不过还有些小问题,直接提出来给 Claude3.5:
这里两个问题:
- 第一次点击生成图形的时候,先出来了一个竖条,卡在那里,等了下才出现动态库;
- 后续点击生成图形的时候,之前的没完成的还在继续,导致会两个叠加在一起
第一个问题一次性完美解决,在设置初始 chartOption 时就包含第一年的数据,这样图表一开始就会显示有意义的数据。第二个问题,开始的提示词可能不太清晰,导致第二次点击后数据是慢慢回到开始,接着补充了详细提示,问题也很快解决。
至此,一个能跑的版本出来啦!
导出 GIF 的难点
为了方便导出 GIF,接着先简单提示:
这里可以支持导出竞速动态图吗?整个竞速完整过程生成 gif 或者 webp 动态图,支持下载
给出的第一个方案是使用 html2canvas 库来捕获图表的每一帧,然后使用 gif.js 库将这些帧合成为 GIF 动画。第一个版本不行,点了生成 gif 后一直卡在正在生成 GIF..。于是接着完善提示词:
我想要的是:
- 生成 gif 的时候,不影响现在站点上的竞速图;现在会有两份竞速图不断覆盖,这样不对
- 生成 gif 在后台,不要等真的每秒这样的过,要支持快速生成完整的竞速图。 生成完整后就弹出保存窗口,可以保存 gif
中间反复生成了几个版本,都没能解决。于是尝试让 AI 分析下这里的原因,然后提供日志来排查下。接着 Claude 在代码中添加了不少日志,生成的时候如下:
这里一直卡在 Gif 渲染了。把日志也给 Claude3.5 看了下,但是还是没定位到问题。这里让 Claude3.5 反思下有哪些可能导致这里的问题,给出了一堆:
- 图像加载:gif.js 需要完全加载的图像才能正确渲染。
- canvas 大小:确保 canvas 的尺寸正确设置。
- 帧的添加方式:确保正确地将帧添加到 GIF 对象中。
- 内存使用:大量帧可能导致内存问题。
- 异步操作:确保所有帧都已添加后才调用 render() 方法。
逐个排除了下,还是有问题。去看了下 gif.js 的官方文档,依旧没什么头绪。又耐着心去看下 AI 生成的代码,发现在创建 jif 对象的时候,是这样的:
const gif = new GIF({
workers: 4,
quality: 10,
width: canvasWidth,
height: canvasHeight,
workerScript: '/path/to/gif.worker.js' // 确保正确设置 worker 脚本路径
});
问了下 Claude3.5 这个文件的作用,以及如何设置。gif.worker.js 文件包含了 GIF 编码的核心逻辑。它在 Web Worker 中运行,允许 GIF 编码过程在后台进行,不会阻塞主线程。在 nextjs 项目中安装 gif.js 库后,可以在 node_modules 中找到 gif.worker.js 文件,将它复制到 public 目录下,然后设置为 /gif.worker.js
。
看起来很对,但跑了还是不行!又尝试了几轮对话,始终卡在渲染 GIF 那里。Claude3.5 似乎钻进了牛角尖,接着重新开一个对话,换 GPTo1 来试试。提示词也很少:
已生成并添加第 80 帧 content.js:241 已生成并添加第 81 帧
content.js:224 所有帧生成完毕,开始渲染 GIF
content.js:226 调用 gif.render()
content.js:250 GIF 渲染开始
content.js:252 GIF 渲染进度: 0.00%这里渲染 gjf 一直卡在渲染进度 0 ,似乎没有开始。帮我解决
哈哈,这里甚至拼错了 gif 为 gjf,但 GPTo1 还是很快理解了,并给出了方向:
看起来您的 GIF 渲染过程卡在了进度 0.00%,这通常与 gif.worker.js 的加载问题有关。以下是一些可能的原因和解决方法:
- 确认 gif.worker.js 的路径
确保 gif.worker.js 文件位于正确的位置,并且路径 /gif.worker.js 是可以访问的。如果您将 gif.worker.js 放在 public 文件夹中,确保它可以通过浏览器访问。例如,访问http://localhost:3000/gif.worker.js
应该能看到该文件内容。- 检查浏览器控制台错误
打开浏览器的开发者工具,查看是否有关于 gif.worker.js 加载的错误。如果有 404 错误,说明路径不正确;如果有跨域问题,可能需要调整服务器配置。
试了下,发现访问的时候路径被重定向了。忽然想起来,当时为了支持旧的没有带语言版本的链接时,在 middleware.js 中加了自动重定向,会在链接中增加语言。比如访问 https://gallery.selfboot.cn/tools/chartrace 会自动重定向到 https://gallery.selfboot.cn/en/tools/chartrace/ 。这里修复比较简单,去掉 gif.worker.js 的自动重定向,再次尝试导出就可以了。
其他细节优化
大的功能点都完成了,下面就是用 cursor 来优化下细节。不得不说,单纯完成小的细节任务,claude 3.5 正确率还是很高的。比如:
- 修改生成动态 GIF 图的背景颜色;
- 下载文件的名字改为上传文件名,后缀是 gif;
- 把导出 gif 按钮放到右边设置页,导出的时候支持进度条展示进度;
- 支持自定义输入标题;
- 每条数据的颜色可以随机生成,然后保证颜色搭配合理;
- 修改代码支持多语言,生成翻译文件。
这些任务要是自己来写的话,虽然也能实现,但还是要花不少时间的。用 Claude3.5 完成这些细节任务,就轻松很多了。
Cursor Claude3.5 缺陷
在使用的过程中,发现 cursor 还是有不少问题的。生成代码后,点击 Apply,发现变更的 diff 中,有时候会把已经写好的代码删掉,用注释来替换了。所以对于变更,还是不能无脑 Apply,需要人工检查下才行。
此外写的过程中,比如你随手改了一个地方。后续 AI 生成的时候,可能又给你改回去。比如下图中,我已经把这里的时间改成了 500ms,后续每次 Apply 的时候,都会改回去 2000:
猜测这里可能是 cursor 选择代码版本的时候,并没有用当前编辑器中的版本。而是自己在上下文维护了一个代码版本,然后基于维护的版本生成新的代码。之后在 diff 的时候,用了老的版本来覆盖编辑器改动过的版本。
还有一个很不爽的地方就是,Claude3.5 在功能改动的时候,经常忘记删掉多余的代码。比如一个功能已经改变了实现,经常只是增加代码,没有删除掉不用的代码。比如下图,这里渲染的时候没有用到一些状态,但是还在。只能提示它去删掉,好在稍微提示下,它就立马知道怎么删除了。
使用过程反思
找到和 Claude3.5 的正确沟通方法后,从零写一个工具还是挺快的。只用专注于整体的实现思路,然后尽量把任务拆分,每一步 AI 基本完成的还不错。遇到有 Bug 的地方,稍微看看代码,然后也基本也能解决。
有时候单个模型可能钻牛角尖,可以试着换个模型。这次生成 GIF 的时候,用 Claude3.5 排查了半天也没能解决,后来切换到 GPTo1,重新开了个会话,直接就给了很好的排查方向,一下子也就解决问题了。
再分享一个个人小技巧,有的对话长了后,AI 每次提问会带很多上下文,回答的准确度会下降不少。这时候重新开一个新的对话,效果可能会好很多。
你想要什么工具吗?试试用 cursor 来实现吧。