从getContext报错说起:Cursor 里 TextParticleEffect 的 canvas 时序问题
在 Cursor 的 Composer 模式里让 AI 从零生成 Vue3 + Vite + Canvas 的文字爆炸页面,最容易卡住的一步不是代码写不出来,而是点击「开始爆炸」时控制台直接抛出:
Uncaught TypeError: Cannot read properties of undefined (reading 'getContext') at new TextParticleEffect (TextParticleEffect.js:33:29) at TextExplosion.vue:34:16这条报错的意思是:new TextParticleEffect(...)执行时,构造函数内部第 33 行对某个变量调用了.getContext('2d'),但这个变量是undefined。在 Canvas 场景里,几乎可以断定是canvas这个 DOM 引用还没挂上,就被拿去取 2D 上下文了。本文就围绕这条报错,讲清楚怎么在 Cursor 里配好模型通道(用 TaoToken 的 Base URL 和 Key),再让 Composer 按正确的挂载时序把TextParticleEffect.js改对,最后用「爆炸改成 5s」「文本先显示 1s 再爆炸」两轮需求验证修复是否彻底。
TaoToken 在这里只做一件事:提供 OpenAI 兼容的 Key 和 Base URL,让 Cursor 的 Composer 和 chat 两个模式都能正常发请求。它不替你写 Vue,也不替你改 Canvas 逻辑。官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end
一、原问题与场景:为什么getContext会打在 undefined 上
先把报错链路还原清楚。TextExplosion.vue第 34 行大概是这样的调用:
const effect = new TextParticleEffect(this.$refs.canvas, text)而TextParticleEffect.js第 33 行附近是:
constructor(canvas, text) { this.canvas = canvas this.ctx = canvas.getContext('2d') // 第 33 行 // ... }问题出在this.$refs.canvas此刻是undefined。常见原因有三类:
- 模板里没写
ref="canvas",或者写成了别的名字,$refs里根本没有这个键。 - 在
setup或mounted之前就 new 了实例,DOM 还没渲染,ref 自然为空。 v-if控制的 canvas 还没显示,$refs拿不到未挂载的节点。
原文里 AI 第一版代码往往把new TextParticleEffect直接写在点击事件里,但 canvas 元素被v-if包着,或者 ref 名字对不上,于是每次点「开始爆炸」都复现同一条报错。更麻烦的是,后面几轮「动画改成 5s」「文本先显示 1s 再爆炸」的调整,AI 只改了动画参数,没动挂载时序,报错一直没修干净,需求验证也就无从谈起。
所以正确的排障顺序是:先让 Cursor 核对 canvas ref 的挂载时序,把getContext的调用挪到 canvas 确定存在之后,再谈动画节奏。
二、TaoToken 前置:给 Cursor 配一条能用的模型通道
在让 Cursor 改代码之前,先保证它的请求能发出去。打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册账号,在控制台创建一个 API Key。然后进 Cursor 的模型设置,启用自定义 OpenAI 兼容入口:
- Base URL:
https://taotoken.net/api(注意不带/v1,也不加任何 UTM 参数) - API Key:填刚创建的那把,形如
YOUR_API_KEY
配好之后,Composer 和 chat 两个模式的请求都会从这条通道走。这一步只解决「模型能不能回话」,不解决「代码写得对不对」。如果 Key 或 Base URL 填错,表现是请求直接失败或超时,而不是getContext报错——两者要分开判断。
需要管理多把 Key 或查看用量,可以进控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
三、可复制配置:把报错原文和上下文一起交给 Composer
配好通道后回到 Composer,不要只说「帮我修一下报错」。把报错原文、文件路径、行号上下文一起贴进去,让 AI 有足够信息定位时序问题。可以这样组织:
当前项目点击「开始爆炸」时报错: Uncaught TypeError: Cannot read properties of undefined (reading 'getContext') at new TextParticleEffect (TextParticleEffect.js:33:29) at TextExplosion.vue:34:16 TextParticleEffect.js 第 33 行是 this.ctx = canvas.getContext('2d') TextExplosion.vue 第 34 行是 new TextParticleEffect(this.$refs.canvas, text) 请先核对 canvas ref 的挂载时序,确认 $refs.canvas 在 new 实例时是否已经存在, 再给出修改方案。不要只改动画参数。如果 Cursor 需要读取项目文件,确保工作区已经打开对应目录。涉及模型 ID 的选择,可以在模型对话页确认当前可用模型:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite
四、验证请求与成功结果:两轮追加需求做回归
修完时序后,不要只看报错消失就结束。按原文那两轮需求继续追加,验证修复是否彻底:
第一轮:帮我将文字爆炸的动画时间调慢一些,比如调整成 5s 爆炸完毕
第二轮:能否设置要爆炸的文本先显示 1s,然后再开始爆炸。因为现在看不出是哪个文本爆炸的
如果时序修对了,这两轮调整应该只改动画参数和延迟逻辑,不再触发getContext报错。点击「开始爆炸」后,控制台干净,文本先静止显示约 1 秒,再进入约 5 秒的爆炸过程。若第二轮又冒出getContext,说明 AI 在改延迟逻辑时把实例化时机又挪回了 DOM 挂载之前,需要再次强调「先确认 canvas 存在再 new」。
验证通过后,如果后续要长期用 Cursor 做编码和 Agent 任务,可以考虑 Coding Plan,减少频繁换 Key 的麻烦:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite
五、本篇常见错排查
报错依旧出现在第 33 行:说明 AI 只改了调用处,没改构造函数内部。让它在getContext前加一层判断,或把实例化挪到onMounted/nextTick之后。
$refs.canvas始终为空:检查模板里ref名字是否和 JS 里一致,检查 canvas 是否被v-if包着。可以临时改成v-show验证。
请求失败而非报错:多半是 Base URL 带了/v1或 Key 填错。Base URL 必须是https://taotoken.net/api,Key 用YOUR_API_KEY对应的那把。Key 管理页:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite
改了 5s 没生效:确认 AI 改的是实际驱动动画的时长变量,而不是注释或未使用的常量。
接入文档对照:配置细节可查 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
六、语义一致 CTA
这条getContext报错的根因是 canvas 挂载时序,不是模型通道问题。但要让 Cursor 稳定地把时序改对,前提是 Composer 的请求能正常发出去。从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 拿到 Key,Base URL 填https://taotoken.net/api,Cursor 的 chat 和 Composer 就都走这条通道。排障和接入细节看 API Keys 与接入文档;验证模型是否可用去模型对话页;长期编码和 Agent 任务用 Coding Plan。