ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

用Claude打造会断电自救的赛博城市:单文件HTML与状态机实战

用Claude打造会断电自救的赛博城市:单文件HTML与状态机实战 用 Claude 生成一个单文件 HTML 项目现在已经是很常见的玩法。真正有意思的是当这个 HTML 文件里装下一座完整的赛博城市并且城市在断电之后还能自动切换应急照明、启动备用电源、再逐步恢复供电整个项目的技术含量就完全不一样了。这类项目非常适合用来练习 AI 辅助前端开发因为它同时涉及 HTML 结构、CSS 霓虹视觉、JavaScript 动画循环和一套简单的状态机逻辑而 Claude 恰恰擅长在这些领域快速给出可运行的原型。这篇文章会从零拆解这个“会断电自救的赛博城市”项目先说明单文件 HTML 和断电自救机制的设计思路再讲如何准备 Claude Code 环境、如何设计提示词让 Claude 现场生成代码最后给出运行验证、常见报错排查和扩展建议。读完你不仅能把这座赛博城市跑起来还能把同样的提示词方法复用到其他单文件可视化项目上。1. 先搞清楚“会断电自救的赛博城市”到底是什么1.1 单文件 HTML 为什么适合做这类演示单文件 HTML 指的是把 HTML、CSS、JavaScript 全部写在一个.html文件里不依赖构建工具、不用安装 npm 包、不需要启动本地服务器双击文件就能在浏览器里运行。对赛博城市这种偏视觉、偏演示性质的项目来说这是最合适的载体。它的优势很直接交付成本低一个文件就是整个项目。运行门槛低只要能打开浏览器的机器就能跑。便于分享把文件发给别人就能看到效果。便于 Claude 生成Claude 可以在一次输出里完整返回页面的三部分代码。代价也很明确不适合复杂业务、没有模块隔离、代码量一大就不好维护。所以单文件 HTML 适合的是“演示原型”“创意动画”“小型工具页”而不是生产级应用。1.2 “断电自救”在代码里对应什么“断电自救”不是物理概念而是一个用代码模拟的城市电力调度逻辑。整座城市被抽象成一个电力系统系统有多个运行阶段正常供电所有建筑灯光正常霓虹灯闪烁雨丝可见。全城停电市电中断绝大多数窗户和霓虹灯熄灭。应急供电城市进入自救状态部分建筑亮起红色应急灯街道出现闪烁的警示灯。恢复供电电网抢修完成灯光按比例逐步恢复直到回到正常供电。这个流程在代码里就是一套状态机。用一个变量记录当前阶段用另一个计时器控制阶段切换再用一个powerLevel数值控制全局亮度。理解了这个抽象后面读生成代码时会非常轻松。1.3 Claude 在这里承担什么角色Claude 的角色是“翻译器”和“初稿工程师”把自然语言需求翻译成可运行的代码。真正的架构决策仍然由开发者完成。实际项目中需要你做的三件事把需求拆细告诉 Claude 要生成什么效果、有哪些约束。检查生成结果确认逻辑正确、能运行。迭代优化让 Claude 在现有代码上增加功能而不是推翻重来。也就是说Claude 负责写代码你负责定义问题和把关质量。下面所有步骤都围绕这个分工展开。2. 生成前先确认环境和接入方式2.1 Claude 网页版与 Claude Code 怎么选生成这类项目有两种主流方式Claude 网页版和 Claude Code。它们的区别会影响你后面的操作流程。维度网页版Claude Code使用场景一次性对话生成代码片段在项目目录里持续迭代、读写文件交互方式浏览器对话终端命令行可读取本地项目适合人群快速验证想法需要反复调代码、修报错的开发者文件操作需要手动复制代码保存可以直接生成或修改本地文件环境依赖只需要浏览器需要 Node.js 环境和客户端安装如果只是想要一个 HTML 文件网页版完全够用。但如果你想在本地不断修改、让 Claude 检查运行结果并修复问题Claude Code 会更顺手。这篇文章以 Claude Code 作为主要演示环境因为它的迭代方式更接近真实开发流程。2.2 Claude Code 环境检查清单安装并首次使用 Claude Code 前建议按顺序检查下面几项。不要跳过很多后面遇到的报错都来自环境不完整。node -v npm -v claude --versionnode -v确认 Node.js 已安装常见可用版本是 18 及以上。npm -v确认 npm 可用Claude Code 的安装和更新依赖它。claude --version确认 Claude Code 命令能正常执行。如果执行claude --version时报错error: claude native binary not installed说明安装过程没有完整执行一般是 postinstall 脚本没有运行或者 PATH 环境变量没有更新。处理思路是重新执行官方安装命令然后重启终端必要时检查全局安装目录是否在 PATH 中。具体命令以 Claude 官方文档为准不同系统的处理方式略有差异。注意安装和配置客户端时只使用官方提供的安装方式。如果终端报错优先按官方文档的排错说明处理不要自行修改系统关键配置。另外很多开发者会把 Claude Code 接到 VSCode 里使用主要是通过 VSCode 的终端调用claude命令。如果在 VSCode 终端里提示命令找不到而系统终端正常通常是 VSCode 没有继承更新后的 PATH。重启 VSCode 一般就能解决。2.3 给项目定输入、输出和验收标准在写提示词之前先明确这个项目的输入输出和验收标准。这一步看似简单却能显著提高生成质量。输入一句话需求例如“用单个 HTML 文件生成赛博城市夜景”。输出一个cyberpunk-city.html文件。验收标准双击文件能直接打开能看到城市天际线和动态灯光有“触发断电”按钮断电后能自动进入应急状态并恢复。把验收标准写清楚Claude 在生成时就会主动考虑“怎么证明功能正常”这件事而不是只输出一段视觉代码。3. 用提示词让 Claude 从 0 生成赛博城市骨架3.1 第一轮提示词先要骨架第一轮不要追求完整目标是拿到一个能打开、能看到城市画面的 HTML 文件。提示词里要包含四类信息文件形态、视觉元素、控制入口、运行约束。请用单个 HTML 文件实现一个赛博城市夜景演示。要求 1. 使用 Canvas 绘制城市天际线建筑有高有低错落分布 2. 建筑上有随机亮灭的窗户街道有霓虹灯和雨丝效果 3. 页面左侧有一个“触发断电”按钮和一个状态指示文字 4. 代码不依赖任何外部库和 CDN直接双击 HTML 文件即可运行。关键点是第 4 条。不限制依赖时Claude 可能会引入外部库生成后在无网环境里打不开。明确“不依赖任何外部库”能保证输出是纯 HTML、CSS、JavaScript。3.2 第一轮先验收“能打开”把生成结果保存为cyberpunk-city.html双击打开。这一步只验证三件事页面是否出现城市画面。画面是否有动态效果。按钮和状态文字是否显示。如果这三条都满足第一版就合格了。不要在第一次生成时就要求断电自救、天气变化、声音效果全部到位。一次性要求太多Claude 容易顾此失彼生成结果反而更难调整。3.3 第二轮迭代加入断电自救第一版确认没问题后再让 Claude 在现有代码上增加功能。这一步的关键是明确告诉 Claude“在现有基础上增加”而不是让它重新写一份。在现有代码上增加“断电自救”机制 1. 点击“触发断电”后城市进入全城停电状态绝大多数窗户和霓虹灯熄灭 2. 停电 1 秒后部分建筑亮起红色应急灯街道出现闪烁的备用电源警示灯 3. 停电 4 秒后电网抢修完成灯光按比例逐步恢复 4. 用一行状态文字显示当前阶段正常供电 / 全城停电 / 应急供电 / 恢复供电。这里把每个阶段的时间点都写清楚了Claude 就能对应生成状态切换逻辑而不是靠猜。3.4 提示词设计的常见坑给 Claude 写需求时下面四种写法最容易出问题。一次要求太多。正确做法是拆成多轮每轮只加一个明确功能。不说明验收方式。Claude 不知道“成功”长什么样生成结果容易偏。不限定依赖。生成结果可能依赖 CDN离线打不开。迭代时不说“在现有代码上增加”。Claude 可能重新生成一份丢了你上一轮已经验收的内容。提示如果 Claude 生成的代码反复不符合要求优先检查提示词里是否缺少“验收标准”和“约束条件”而不是让 Claude 再猜一次。4. 读懂生成的核心代码状态机是断电自救的关键4.1 HTML 与 CSS页面骨架和霓虹灯效生成的 HTML 结构通常比较简单一个控制面板、一个画布、一个脚本块。下面的骨架是常见形态。!doctype html html langzh-cn head meta charsetutf-8 title赛博城市断电自救演示/title style body { margin: 0; background: #0a0a16; overflow: hidden; } canvas { display: block; width: 100vw; height: 100vh; } #control { position: fixed; left: 14px; top: 14px; z-index: 10; padding: 8px 12px; border: 1px solid #22d3ee; border-radius: 6px; background: rgba(0, 0, 0, 0.72); color: #7dd3fc; font-family: monospace; font-size: 14px; display: flex; gap: 10px; align-items: center; } button { background: #0f172a; color: #67e8f9; border: 1px solid #22d3ee; border-radius: 4px; padding: 4px 10px; cursor: pointer; font-family: monospace; } /style /head body div idcontrol span城市电网/span button idblackoutBtn触发断电/button span idstatusText正常供电/span /div canvas idcityCanvas/canvas script // 核心逻辑见 4.2 /script /body /html控制面板是普通 DOM 元素城市画面是 Canvas 绘制。按钮和状态文字留在 DOM 里是因为它们要被用户直接点击和阅读用 DOM 处理最简单城市夜景需要每帧更新用 Canvas 性能更好。4.2 JavaScript城市渲染与电源状态机下面是这个项目最核心的脚本。它做了三件事生成城市数据、维护电源状态机、绘制画面。const canvas document.getElementById(cityCanvas); const ctx canvas.getContext(2d); const statusText document.getElementById(statusText); const blackoutBtn document.getElementById(blackoutBtn); const phaseNames { normal: 正常供电, blackout: 全城停电, emergency: 应急供电, restoring: 恢复供电 }; let phase normal; let phaseTimer 0; let powerLevel 1; function resize() { canvas.width window.innerWidth; canvas.height window.innerHeight; } const buildings []; const raindrops []; const groundY () canvas.height * 0.8; function initCity() { buildings.length 0; raindrops.length 0; let x 0; for (let i 0; i 22; i) { const w 50 Math.random() * 80; const h 100 Math.random() * 260; const windows []; const cols Math.floor(w / 16); const rows Math.floor(h / 24); for (let r 0; r rows; r) { for (let c 0; c cols; c) { windows.push({ x: x 10 c * 16, y: groundY() - h 18 r * 24, lit: Math.random() 0.82, emergency: Math.random() 0.15 }); } } buildings.push({ x, y: groundY() - h, w, h, windows }); x w 8; } for (let i 0; i 120; i) { raindrops.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, len: 12 Math.random() * 10, speed: 220 Math.random() * 180 }); } } function update(dt) { phaseTimer dt; if (phase blackout phaseTimer 1.2) { phase emergency; phaseTimer 0; } else if (phase emergency phaseTimer 4) { phase restoring; phaseTimer 0; } else if (phase restoring phaseTimer 2) { phase normal; phaseTimer 0; powerLevel 1; } statusText.textContent phaseNames[phase]; } function drawWindow(win, alpha) { if (alpha 0.05) return; ctx.globalAlpha alpha; ctx.fillStyle win.emergency ? #ff503c : #ffdc82; ctx.fillRect(win.x, win.y, 7, 10); ctx.globalAlpha 1; } function draw(now) { const sky ctx.createLinearGradient(0, 0, 0, canvas.height); sky.addColorStop(0, #04040f); sky.addColorStop(1, #1b1033); ctx.fillStyle sky; ctx.fillRect(0, 0, canvas.width, canvas.height); buildings.forEach((b) { ctx.fillStyle #111827; ctx.fillRect(b.x, b.y, b.w, b.h); b.windows.forEach((win) { if (!win.lit) return; let alpha 0.9 * powerLevel; if (phase blackout) alpha 0.05; if (phase emergency) alpha win.emergency ? 0.85 : 0.1; if (phase restoring) alpha 0.35 0.65 * (phaseTimer / 2); drawWindow(win, alpha); }); }); ctx.fillStyle #0b0b14; ctx.fillRect(0, groundY(), canvas.width, canvas.height * 0.2); ctx.strokeStyle rgba(120, 200, 255, ${0.25 * powerLevel}); ctx.lineWidth 1; raindrops.forEach((d) { ctx.beginPath(); ctx.moveTo(d.x, d.y); ctx.lineTo(d.x - 8, d.y d.len); ctx.stroke(); }); if (phase emergency) { const blink 0.5 0.5 * Math.sin(now / 150); ctx.fillStyle rgba(255, 40, 40, ${blink}); ctx.fillRect(canvas.width / 2 - 3, groundY() - 18, 6, 18); } } function loop(now) { const dt Math.min(now - (loop.last || now), 50) / 1000; loop.last now; update(dt); draw(now); requestAnimationFrame(loop); } blackoutBtn.addEventListener(click, () { if (phase normal) { phase blackout; phaseTimer 0; powerLevel 0; } else { phase normal; phaseTimer 0; powerLevel 1; } }); window.addEventListener(resize, () { resize(); initCity(); }); resize(); initCity(); requestAnimationFrame(loop);这段代码最值得学习的是状态机。phase表示当前阶段phaseTimer记录当前阶段已经持续的时间powerLevel表示全局供电比例。三个变量配合就构成了完整的“断电自救”行为点击按钮后phase变成blackoutpowerLevel置 0。update里检测到blackout持续 1.2 秒后切到emergency。应急阶段持续 4 秒后切到restoring。恢复阶段持续 2 秒后回到normalpowerLevel恢复为 1。绘制时的核心逻辑在drawWindow的alpha计算里。正常阶段窗户亮停电阶段几乎全灭应急阶段只有emergency标记为真的窗户亮红灯恢复阶段亮度按时间逐步上升。这就是视觉上“自救”的实现方式。4.3 触发方式按钮、定时器与随机事件上面示例里断电靠按钮触发。实际演示场景还可以扩展成定时或随机触发让城市在无人操作时也出现“突发断电又自救”的效果。setTimeout(function schedule() { blackoutBtn.click(); setTimeout(schedule, 30000 Math.random() * 30000); }, 30000 Math.random() * 30000);这段代码让城市每隔 30 到 60 秒随机断电一次。用于大屏展示或无人值守演示时会比手动点击更自然。5. 本地运行与功能验证5.1 直接打开 HTML 文件生成的文件不需要构建直接双击即可打开。在终端里也可以用命令打开方便在 Claude Code 工作流里快速验证。# macOS open cyberpunk-city.html # Windows start cyberpunk-city.html如果文件路径包含空格或中文终端打开时记得加引号。浏览器打开后按 F12 打开开发者工具切到 Console 面板确认没有红色报错。5.2 验证正常供电阶段正常供电阶段主要确认三件事城市天际线完整建筑高低错落。窗户有随机亮灭雨丝持续下落。状态文字显示“正常供电”。如果画面静止不动优先检查requestAnimationFrame循环是否在运行。最常见的错误是脚本里存在抛异常导致渲染函数在第一帧就中断了。5.3 验证断电自救三个阶段点击“触发断电”按钮后按下面的时间表观察画面确认状态机按预期工作。时间点状态文字屏幕表现点击瞬间全城停电大部分灯光熄灭雨丝变淡1.2 秒后应急供电红色应急灯亮起警示灯闪烁5.2 秒后恢复供电灯光逐步变亮7.2 秒后正常供电完全恢复雨丝重新清晰验证时要特别注意“恢复供电”这一阶段。很多生成结果在断电后不会自动恢复原因通常是状态机缺少恢复分支这个在排查部分会展开讲。注意不要只验证程序能启动还要验证输入、输出、异常分支和日志是否符合预期。断电自救项目里“恢复供电”就是最容易遗漏的异常分支。6. 生成代码后容易踩的坑6.1 常见问题表这类项目生成后最常遇到的问题集中在下面几种问题现象可能原因排查方式处理建议页面全黑无画面Canvas 尺寸为 0或脚本抛异常F12 打开 Console 看报错确认 canvas 有宽高检查 resize 逻辑按钮点击没反应事件绑定先于 DOM 渲染检查 script 是否在 body 末尾把 script 放到 body 最后或改用 defer断电后灯全灭永远不恢复状态机缺少恢复分支检查 phase 是否能进入 restoring在 update 中增加恢复计时逻辑浏览器提示外部资源加载失败代码引入了 CDN 依赖查看 Network 面板改为纯原生实现移除外部依赖雨丝消失但画面正常断电时 powerLevel 为 0检查雨丝的透明度计算给雨丝单独保留最低可见度6.2 error: claude native binary not installed 的排查路径这是使用 Claude Code 时常见的环境报错现象是执行claude命令时直接提示二进制没有安装。排查顺序先运行claude --version确认是否所有终端都报错。运行which claude确认命令所在路径。检查系统终端正常但 VSCode 终端异常的情况优先重启 VSCode。重新执行官方安装命令确认 postinstall 脚本完整运行。这个报错的根本原因通常是安装中断或 PATH 未刷新不是代码问题。修复环境后之前生成的 HTML 文件不受影响。6.3 HTML 打不开或画面空白如何排查如果双击 HTML 文件没有画面按以下顺序排查检查文件内容是否完整确认/html结束标签存在。用浏览器打开开发者工具查看 Console 是否报 JavaScript 错误。确认 canvas 元素存在且脚本能正确获取到元素引用。如果文件路径特殊尝试用本地服务器方式打开。# 在项目目录启动一个简单静态服务器 python3 -m http.server 8080然后访问http://localhost:8080/cyberpunk-city.html。用服务器打开的好处是能排除文件路径、浏览器本地文件权限等问题。7. 从演示项目到工程化最佳实践和扩展7.1 学习环境与生产环境的区别这个项目作为学习演示时单文件方案最合适。但一旦要把它变成真正的产品功能就需要做工程化改造。维度学习演示生产环境代码组织单文件拆分 HTML、CSS、JS使用模块化状态管理全局变量独立的状态管理模块或类测试手动点击验证状态机单元测试性能少量建筑和粒子关注帧率、内存占用、设备性能可维护性便于阅读需要注释、文档、代码规范这个项目里最值得优先工程化的部分就是状态机。把phase、phaseTimer、powerLevel抽成一个独立的类配上单元测试后续加任何新阶段都会容易很多。7.2 可复用的检查清单每次用 Claude 生成类似单文件项目时建议照下面的清单过一遍。生成前[ ] 明确输出文件形态是单个 HTML。[ ] 明确是否允许外部依赖。[ ] 明确验收标准至少包含“如何确认功能正常”。[ ] 把复杂需求拆成多轮提示词。生成后[ ] 双击文件能直接打开。[ ] Console 面板无红色报错。[ ] 所有按钮和交互有响应。[ ] 断电、应急、恢复三个阶段都实际验证过。[ ] 窗口缩放后画面正常重建。排查时[ ] 先看 Console 报错。[ ] 再检查事件绑定顺序。[ ] 然后检查状态机是否有遗漏分支。[ ] 最后确认是否引入了外部依赖。7.3 可以继续扩展的方向这座赛博城市本身是一个很好的原型可以往多个方向扩展。把状态机参数化用配置项控制每个阶段的持续时间和灯光颜色。加入粒子效果例如断电瞬间的电流火花。增加声音反馈通过 Web Audio API 模拟电力故障音。把城市灯光和真实数据关联例如用某个指标的变化控制城市亮度。做成一个小游戏玩家需要在断电后手动“抢修”电网。同样的提示词方法也适用于其他单文件 HTML 创意项目比如节日动画、数据大屏、个人主页等。核心方法是一致的先要骨架再迭代功能最后用状态机和数据驱动视觉变化。对新手来说最有价值的练习不是把提示词写得更长而是先自己读懂生成代码里的状态机再试着改一个参数、加一个阶段把生成结果变成真正能掌控的代码。这样才能把“Claude 写出来的项目”变成“自己会写的项目”。
返回列表