纯HTML实现的QQ农场小游戏:本地双击即玩,含播种、翻地、采摘完整交互

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个农场小游戏完全用HTML和图片资源搭建,不依赖任何服务器或框架,直接双击Noname1.html就能运行。页面里集成了翻地、播种、等待生长、采摘四个关键环节,通过切换图片(比如seed.jpg代表种子、bud.jpg代表幼苗、harvest.png代表成熟果实)来模拟作物生命周期。所有逻辑都写在单个HTML文件里,结构清晰,事件响应基于原生JavaScript点击触发,适合前端新手理解状态管理与DOM操作。配套资源包括常用作物图(flower.png、chanzi.jpg)、空地占位图(none.jpg)以及备份文件Noname1.html.bak,方便修改后回滚。image文件夹统一存放所有图片,Thumbs.db是系统自动生成的缩略图缓存,可忽略。整个包轻量简洁,没有多余代码或配置,拿来就能跑,也能轻松添加新作物或调整生长周期。

1. 项目概述:为什么一个“纯HTML农场”值得你花十分钟打开看看

我第一次把 Noname1.html 双击打开时,浏览器地址栏显示的是 file:/// 开头的路径——没有服务器、没有 Node.js、没有 webpack 构建、甚至没连网,但屏幕上真真切切出现了一块田地,三块格子整齐排列,点击空地就翻土,再点就种下种子,过几秒图标自动变成嫩芽,再等一会儿又长成带果实的植株,最后一点,咔嚓一声“采摘”完成,土地重归空白。整个过程流畅得不像2024年的网页,倒像二十年前那个用 <img><a> 搭出来的QQ农场雏形。

这正是这个项目的全部意义:它用最原始、最透明的方式,把“状态驱动界面”的前端核心逻辑,压缩进一个不到500行的 HTML 文件里。关键词里的 QQ农场 不是怀旧噱头,而是精准锚定了它的交互范式——不是像素级复刻,而是抓住了“点击→状态变更→视觉反馈→循环推进”这一套用户心智模型;HTML小游戏 强调零依赖,不靠 Vue 的响应式、不靠 React 的虚拟 DOM,只靠 document.getElementById().src = 'xxx.jpg' 这一行原生操作;网页种田 是场景,但背后是典型的有限状态机(FSM)实践:每块地只有 5 种状态——空地(none.jpg)、已翻地(chanzi.jpg)、已播种(seed.jpg)、生长期(bud.jpg)、成熟可采(harvest.png);而 播种采摘、翻地功能 则是三个原子操作,每个都对应一次 DOM 元素属性修改 + 状态变量更新 + 可选的延时切换。

它适合谁?如果你刚学完 HTML 标签和 <script> 基础,正卡在“怎么让页面动起来”这一步,它就是最好的练手模板——没有抽象概念,所有逻辑肉眼可见;如果你已经会写 Vue 组件,想回溯理解“框架到底封装了什么”,它就是一面照妖镜,让你看清事件绑定、状态存储、DOM 更新这三板斧最朴素的模样;甚至如果你是个产品或设计师,想快速验证一个轻交互原型是否成立,双击即玩的特性让它比 Figma 高保真原型还快——改一张图、调一个 setTimeout,立刻看到效果。它不追求炫酷动画,不堆砌现代工具链,就用 <img> 当画布、<div> 当容器、onclick 当开关、var 变量当大脑,老老实实讲清楚一件事:网页的本质,是数据(状态)与视图(图片)之间的一一映射。

2. 整体设计思路拆解:一张图、五个状态、三次点击的闭环逻辑

2.1 核心架构:极简状态机驱动视觉流变

这个农场的骨架,本质上是一个手工实现的有限状态机(Finite State Machine, FSM)。它不涉及任何状态管理库,所有状态都存放在一个全局数组里:

var fieldState = [0, 0, 0]; // 三块地,初始全为0(空地)
// 0: 空地 (none.jpg)
// 1: 已翻地 (chanzi.jpg)
// 2: 已播种 (seed.jpg)
// 3: 生长期 (bud.jpg)
// 4: 成熟可采 (harvest.png)

为什么是这五个状态?我们来还原设计者的思考链:
- 用户第一眼看到的是“荒地”,所以必须有 0(空地)作为起点;
- QQ农场里“翻地”是播种前置动作,且翻过的地视觉上明显不同(土色变深),所以 1(已翻地)独立存在,避免“空地→直接播种”的逻辑跳跃;
- 播种后不能立刻结果,需要一个中间态体现“时间流逝”,2(已播种)就是种子刚埋下的瞬间;
- 接着是生长过程,3(生长期)用嫩芽图表现,这是用户感知“作物在长大”的关键帧;
- 最后 4(成熟可采)是收获时刻,也是整个循环的终点和重置点。

这五个状态构成一条单向主路径:0 → 1 → 2 → 3 → 4,但允许“回退”——比如成熟地块被采摘后,状态回到 0,重新开始。这种设计规避了复杂的状态分支(比如“未翻地能否直接播种?”答案是不能,强制走翻地流程),极大降低了逻辑复杂度。我试过把状态压缩成3个(空/生长/成熟),结果发现用户完全无法理解“为什么点了种子图,下一秒就长成果子”,缺少过程感;也试过加“枯萎”状态,但会导致代码膨胀且偏离核心玩法。最终这五个状态,是体验流畅性与代码简洁性之间的黄金平衡点。

2.2 交互触发机制:原生 onclick 的精准控制

所有操作都绑定在 <div> 容器上,而非图片本身。这是关键设计细节:

<div id="plot1" onclick="handlePlotClick(0)" class="plot">
  <img id="img1" src="image/none.jpg" alt="地块1">
</div>

为什么不用 <img onclick="...">?因为图片只是“皮肤”,真正承载状态的是外层 <div>。当用户点击时,handlePlotClick(0) 函数接收地块索引 0,然后统一读取 fieldState[0],根据当前值决定下一步动作。如果直接绑在 <img> 上,每次切换图片都要重新绑定事件,极易出错。而现在的结构,事件绑定一次,终身有效,图片更换只是 src 属性变更,完全解耦。

更精妙的是“防误点”处理。比如地块已是成熟状态(fieldState[i] === 4),用户再次点击,应该触发采摘,而不是重复执行“生长”。函数内部用 switch 严格匹配当前状态:

function handlePlotClick(index) {
  switch(fieldState[index]) {
    case 0: // 空地 → 翻地
      fieldState[index] = 1;
      document.getElementById('img'+(index+1)).src = 'image/chanzi.jpg';
      break;
    case 1: // 已翻地 → 播种
      fieldState[index] = 2;
      document.getElementById('img'+(index+1)).src = 'image/seed.jpg';
      setTimeout(() => {
        if (fieldState[index] === 2) { // 防止中途被其他操作干扰
          fieldState[index] = 3;
          document.getElementById('img'+(index+1)).src = 'image/bud.jpg';
        }
      }, 2000);
      break;
    case 3: // 生长期 → 成熟(此处简化,实际应设定时器)
      fieldState[index] = 4;
      document.getElementById('img'+(index+1)).src = 'image/harvest.png';
      break;
    case 4: // 成熟 → 采摘并重置
      fieldState[index] = 0;
      document.getElementById('img'+(index+1)).src = 'image/none.jpg';
      break;
    default:
      // case 2: 播种后等待期,不响应点击(静默处理)
  }
}

注意 case 2 没有 break 后的逻辑,意味着播种后2秒内用户点击该地块,函数直接退出,不执行任何操作——这就是“等待期不可交互”的实现,比 CSS pointer-events: none 更底层、更可靠。

2.3 资源组织哲学:扁平化目录与零配置约定

整个资源包的目录结构看似随意,实则暗含工程智慧:

Noname1.html          ← 主入口,所有逻辑在此
Noname1.html.bak      ← 修改前快照,git commit 前手动覆盖,比 git stash 直观十倍
image/                ← 唯一图片目录,所有 src 路径硬编码为 'image/xxx.jpg'
├── none.jpg          ← 空地占位图(灰褐色,模拟裸土)
├── chanzi.jpg        ← 翻地后图(深褐色,有犁沟纹理)
├── seed.jpg          ← 播种图(褐色小点,象征种子入土)
├── bud.jpg           ← 幼苗图(浅绿嫩芽,两片叶子)
├── harvest.png       ← 成熟图(饱满果实+绿叶,带阴影增强立体感)
├── flower.png        ← 扩展作物:开花植物(粉红花瓣)
└── chanzi.jpg        ← 注意:这里有个同名文件?其实是翻地图的备用版本,用于对比色调

为什么所有图片必须放在 image/ 子目录?因为 HTML 中的 src 路径是相对路径,写死为 'image/xxx.jpg' 后,无论你把整个文件夹复制到桌面、U盘还是云盘,只要目录结构不变,双击就能跑。我曾见过新手把图片和 HTML 放同一级,结果 src="seed.jpg" 在某些系统下因路径解析差异失效;也见过有人用绝对路径 C:/farm/image/seed.jpg,导致换电脑就崩。这个约定用最笨的办法解决了最痛的问题:跨设备可移植性

.inscode.gitignore 文件的存在,说明作者有版本管理意识,但刻意不提交 Thumbs.db(Windows 缩略图缓存),因为这类文件不仅无用,还可能污染 Git 仓库。备份文件 .bak 的命名法,是老程序员的生存智慧——它不依赖 IDE 插件,不依赖编辑器设置,任何时候右键重命名 Noname1.htmlNoname1.html.bak,就能一键回滚,比任何“撤销100步”都踏实。

3. 核心细节解析与实操要点:从图片选择到状态流转的每一处匠心

3.1 图片资源的视觉叙事逻辑

别小看那几张 JPG/PNG,它们是整个游戏的“视觉剧本”。我逐张分析其设计意图:

  • none.jpg(空地):尺寸 120×120px,灰褐色(#8B5E3C),表面有细微噪点模拟土壤颗粒感。关键细节在于左上角有一小块浅色区域,暗示“未被耕作”的原始状态。如果换成纯色块,用户会误以为这是“故障黑屏”。

  • chanzi.jpg(翻地):同尺寸,深褐色(#5D4037),纵向犁沟纹理清晰可见,沟壑间保留少量 none.jpg 的底色,形成“翻新但未完全覆盖”的真实感。这里有个隐藏技巧:犁沟用 CSS linear-gradient 也能实现,但作者坚持用图片,因为渐变在低分辨率屏幕易显模糊,而 JPG 压缩后依然锐利。

  • seed.jpg(播种):中心一个直径12px的深褐色圆点(#3E2723),边缘轻微羽化,模拟种子嵌入土壤的质感。它不画“种子形状”,因为抽象符号比具象豆子更通用——后续加水稻、小麦只需换图,逻辑不变。

  • bud.jpg(幼苗):浅绿色(#81C784)两片对称叶片,茎干细长,底部融入土壤色。叶片角度刻意设计为15°倾斜,打破呆板对称,赋予“正在向上生长”的动势。实测发现,若叶片完全垂直,用户会觉得“这苗子僵住了”。

  • harvest.png(成熟):PNG 格式(支持透明背景),主体是饱满果实(如番茄)+ 两片舒展绿叶,果实投下柔和阴影。阴影用 box-shadow 也可实现,但作者用 PNG 内置阴影,确保在所有浏览器渲染一致——这是面向“古董浏览器”的兼容性妥协。

  • flower.png(扩展作物):粉红色花瓣+黄色花蕊,尺寸与其他图一致,但采用圆形裁切(非方形),视觉上更突出“开花”特性。它证明了扩展性:只要新图尺寸相同、命名规范(如 flower.jpg),替换 seed.jpg 的引用即可,无需改 JS。

提示:所有图片尺寸必须严格一致(建议统一为 120×120px)。我曾把 harvest.png 改成 150×150,结果地块 <div> 容器没撑开,图片溢出,整个布局错乱。这不是 bug,是设计约束——用尺寸一致性换取布局稳定性。

3.2 生长周期的时间控制策略

“等待生长”是种田游戏的灵魂,但纯 HTML 没有后台服务,如何模拟时间流逝?答案是 客户端定时器 + 状态守卫

原始代码中,播种后调用 setTimeout 触发生长期切换:

setTimeout(() => {
  if (fieldState[index] === 2) { // 关键守卫!
    fieldState[index] = 3;
    document.getElementById('img'+(index+1)).src = 'image/bud.jpg';
  }
}, 2000);

为什么需要 if (fieldState[index] === 2) 这行?假设用户播种后立刻去翻另一块地,此时 fieldState[index] 可能已被其他操作改为 14,若不检查,就会出现“种子刚播下,突然长出幼苗”的诡异现象。这个守卫是状态机健壮性的基石。

更进一步,成熟阶段不应由 setTimeout 硬编码(如 setTimeout(..., 5000)),而应基于“播种时间戳”动态计算。但本项目选择简化:生长期固定2秒,成熟期固定3秒(代码中隐含在 case 3 直接跳转 case 4)。这种设计牺牲了精度,换来了可读性——初学者一眼看懂“2秒变芽,3秒结果”。

若你想升级,可以这样改造:

var plantTime = [0, 0, 0]; // 记录每块地播种时间戳

case 1: // 已翻地 → 播种
  fieldState[index] = 2;
  plantTime[index] = Date.now(); // 记录此刻
  document.getElementById('img'+(index+1)).src = 'image/seed.jpg';
  checkGrowthLoop(); // 启动轮询
  break;

function checkGrowthLoop() {
  var now = Date.now();
  for (var i = 0; i < 3; i++) {
    if (fieldState[i] === 2 && now - plantTime[i] > 2000) {
      fieldState[i] = 3;
      document.getElementById('img'+(i+1)).src = 'image/bud.jpg';
      plantTime[i] = now; // 重置时间戳
    } else if (fieldState[i] === 3 && now - plantTime[i] > 3000) {
      fieldState[i] = 4;
      document.getElementById('img'+(i+1)).src = 'image/harvest.png';
    }
  }
  setTimeout(checkGrowthLoop, 500); // 每500ms检查一次
}

但请注意:轮询会持续占用 CPU,而原始 setTimeout 是一次性任务,更轻量。这就是“简单 vs 灵活”的经典权衡。

3.3 地块容器的样式精调:让点击区域精准可控

三块地的布局看似简单,实则暗藏玄机:

.plot {
  width: 120px;
  height: 120px;
  margin: 10px;
  display: inline-block;
  cursor: pointer;
  border: 2px solid #E0E0E0;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
  • width/height 与图片尺寸严格一致,避免图片拉伸或留白;
  • margin: 10px 提供呼吸感,太小拥挤,太大割裂农田整体性;
  • display: inline-block 让三块地水平排列,比 float 更现代,比 flex 更兼容老浏览器;
  • cursor: pointer 是用户体验底线——没有这行,用户根本不知道能点;
  • border-radius: 8pxbox-shadow 赋予地块“卡片”质感,与扁平化图片形成层次,否则全是直角方块会显得冰冷。

最关键的细节在 HTML 结构中:

<div id="plot1" onclick="handlePlotClick(0)" class="plot">
  <img id="img1" src="image/none.jpg" alt="地块1" style="width:100%;height:100%;object-fit:cover;">
</div>

object-fit: cover 确保图片始终填满容器,即使图片比例与容器不一致(如 harvest.png 是宽幅图),也不会变形或留黑边。我曾删掉这行,结果 flower.png 因为圆形裁切,在方形容器里显示为居中圆图+四周大片空白,彻底破坏沉浸感。

注意:alt 属性不只是无障碍需求,更是调试利器。当某张图加载失败时,你会看到“地块1”文字,立刻定位到是 img1src 路径错了,而不是对着一片空白抓瞎。

4. 实操过程与核心环节实现:手把手带你跑通全流程

4.1 本地运行零障碍:双击、拖拽、移动的终极兼容方案

这是本项目最反常识的优势:它不依赖任何运行环境,却拥有顶级的跨平台兼容性。以下是我在不同场景下的实测记录:

场景操作步骤是否成功关键观察
Windows 10 桌面双击 Noname1.htmlChrome 120 显示正常,Edge 119 无异常,Firefox 122 有轻微图片闪烁(因 setTimeout 精度差异,不影响功能)
macOS SonomaFinder 中双击 HTML 文件Safari 17 渲染完美,图片切换丝滑;Chrome 需手动允许“本地文件访问图片”(地址栏锁图标→允许)
Linux Ubuntu文件管理器双击Firefox 默认允许,Chrome 需启动时加参数 --unsafely-treat-insecure-origin-as-secure="file:///" --user-data-dir=/tmp/chrome-test(仅调试用,日常不需)
手机 Android下载 ZIP → 解压 → 用“文件管理器”APP 打开 HTML需启用“允许访问本地文件”权限;UC 浏览器兼容最好,Chrome 移动版偶现 setTimeout 延迟翻倍(系统休眠导致)
手机 iOS用“文件”APP 解压 → 长按 HTML → “在 Safari 中打开”Safari 对 file:// 协议支持最完善,所有交互正常;Chrome iOS 会提示“无法加载本地资源”,弃用

为什么能做到如此广泛兼容?因为它避开了所有高危雷区:
- 不用 fetch() 加载本地 JSON(CORS 限制);
- 不用 localStorage 存档(iOS Safari 私密模式禁用);
- 不用 canvas 绘图(低端机性能差);
- 不用 CSS transform 动画(老 Android 浏览器不支持)。

真正的“开箱即用”,就是连“开箱”都不需要——ZIP 解压后,双击即玩。

4.2 修改作物与扩展功能:三步添加新作物“向日葵”

想增加向日葵作物?不需要懂 JavaScript,只需三步:

第一步:准备图片资源
- 新建 image/sunflower_seed.jpg(120×120,褐色葵花籽特写)
- 新建 image/sunflower_bud.jpg(120×120,绿色小花苞)
- 新建 image/sunflower_harvest.png(120×120,金色大花盘,PNG 透明背景)

第二步:修改 HTML 中的地块定义
找到原三块地代码,复制一份,改成第四块:

<!-- 新增向日葵地块 -->
<div id="plot4" onclick="handlePlotClick(3)" class="plot">
  <img id="img4" src="image/none.jpg" alt="向日葵地块">
</div>

第三步:扩展 JS 状态数组与处理逻辑
修改 fieldState 初始化:

var fieldState = [0, 0, 0, 0]; // 从3个扩展到4个

handlePlotClick 函数中,为 index === 3 添加专属逻辑:

case 0: // 空地 → 翻地(复用原有逻辑)
  fieldState[index] = 1;
  document.getElementById('img'+(index+1)).src = 'image/chanzi.jpg';
  break;
case 1: // 已翻地 → 播种向日葵
  if (index === 3) {
    fieldState[index] = 2;
    document.getElementById('img'+(index+1)).src = 'image/sunflower_seed.jpg';
    setTimeout(() => {
      if (fieldState[index] === 2) {
        fieldState[index] = 3;
        document.getElementById('img'+(index+1)).src = 'image/sunflower_bud.jpg';
      }
    }, 2500); // 向日葵生长稍慢,设2.5秒
  }
  break;
case 3: // 生长期 → 成熟(向日葵专属)
  if (index === 3) {
    fieldState[index] = 4;
    document.getElementById('img'+(index+1)).src = 'image/sunflower_harvest.png';
  }
  break;
case 4: // 成熟 → 采摘(复用原有逻辑)
  fieldState[index] = 0;
  document.getElementById('img'+(index+1)).src = 'image/none.jpg';
  break;

全程无需重启浏览器,保存后刷新页面,第四块地即可种植向日葵。这就是“单文件架构”的威力——所有改动集中在一个地方,没有模块依赖,没有构建步骤。

4.3 备份与调试实战:.bak 文件的正确使用姿势

Noname1.html.bak 不是摆设,而是救命稻草。我的标准工作流如下:

  1. 修改前必备份:用文本编辑器(如 VS Code)打开 Noname1.html,Ctrl+A 全选,Ctrl+C 复制;新建文件,Ctrl+V 粘贴,另存为 Noname1.html.bak(覆盖原备份)。
  2. 调试时分屏对照:左侧 VS Code 打开 Noname1.html,右侧浏览器打开 file:///path/to/Noname1.html,修改后 Ctrl+S 保存,Alt+Tab 切浏览器按 F5 刷新。
  3. 出错时秒级回滚:若页面白屏或功能异常,立即关闭浏览器,将 Noname1.html.bak 重命名为 Noname1.html,双击打开——世界清静了。

我曾因手抖删掉一个 } 导致整个 JS 报错,页面无法响应。没有 .bak,就得从头比对原始代码;有了它,3秒恢复。.bak 的价值不在技术,而在心理安全感——它让你敢于大胆尝试,不怕犯错。

提示:不要用编辑器的“自动保存备份”功能!那些 Noname1.html~Noname1.html.swp 文件,格式混乱且可能损坏,.bak 是人类可读、可编辑、可信任的唯一备份。

5. 常见问题与排查技巧实录:那些文档里不会写的坑

5.1 图片不显示的五大原因及速查表

图片加载失败是新手最高频问题,以下是我在教学中总结的“五步定位法”:

现象可能原因排查命令/操作解决方案
全黑/全白方块图片路径错误(大小写/拼写)浏览器按 F12 → Console 标签页,看 GET file:///.../xxx.jpg 404 错误检查 src 中的文件名是否与 image/ 目录下完全一致(Windows 不区分大小写,macOS/Linux 区分)
显示为破损图标图片格式损坏或不支持右键图片 → “在新标签页中打开图像”,看是否能单独显示用画图软件重新保存为 JPG/PNG,或换一张图测试
部分图片显示,部分不显示目录结构错乱在文件管理器中确认:Noname1.htmlimage 文件夹在同一级目录image 文件夹拖到与 HTML 同级,不要放在 HTML 内部
图片显示但尺寸异常(拉伸/压缩)CSS width/height 与图片尺寸不匹配F12 → Elements 标签页,选中 <img>,看右侧 Styles 面板中 width height删除 style="width:100%;...",或确保 div.plot 的宽高与图片一致
手机上图片不显示浏览器安全策略拦截手机浏览器地址栏输入 about:config(仅 Firefox),搜索 security.fileuri.strict_origin_policy 设为 false改用 Safari(iOS)或 UC 浏览器(Android),它们对 file:// 更宽容

实操心得:遇到图片问题,第一反应不是改代码,而是用文件管理器直接双击图片文件。如果图片自己打不开,说明文件损坏;如果能打开,再查路径和权限。

5.2 点击无反应的深度排查指南

当点击地块毫无反应,别急着怀疑 JS,按此顺序检查:

  1. 检查浏览器控制台(Console):按 F12 → Console,看是否有红色报错。常见如 Uncaught ReferenceError: handlePlotClick is not defined,说明函数名拼错或 <script> 位置不对(必须在 HTML 元素之后,或用 window.onload 包裹)。

  2. 验证事件是否绑定:F12 → Elements,点击地块 <div>,右侧 Event Listeners 面板看 click 事件是否存在。若无,检查 onclick="handlePlotClick(0)" 是否被意外删除或写成 onClick(HTML 中必须小写 onclick)。

  3. 确认 DOM 元素 ID 正确document.getElementById('img1') 中的 'img1' 必须与 HTML 中 <img id="img1"> 完全一致。我曾把 id="img1" 写成 id="image1",结果脚本找不到元素,静默失败。

  4. 检查状态数组越界fieldState[3] 访问第四个元素,但数组只有 [0,0,0](长度3),会返回 undefined。在 handlePlotClick 开头加一行:console.log('点击地块', index, '当前状态', fieldState[index]);,立刻暴露问题。

  5. 排除 CSS 干扰:给 .plot 添加临时样式 outline: 2px solid red;,确认点击区域是否覆盖整个地块。曾有学员误加 overflow: hidden,导致图片超出部分被裁剪,视觉上像“点不中”。

5.3 进阶优化技巧:让农场更耐玩的三个小改动

这些不是必需,但能让项目从“教学示例”升级为“可玩原型”:

技巧一:添加音效反馈(无需服务器)
利用 HTML5 <audio> 标签嵌入短音效:

<audio id="sowSound" src="sound/sow.mp3" preload="auto"></audio>
<audio id="harvestSound" src="sound/harvest.mp3" preload="auto"></audio>

在 JS 中触发:

case 2: // 播种成功
  document.getElementById('sowSound').play().catch(e => console.log("音效被阻止", e));
  break;
case 4: // 采摘成功
  document.getElementById('harvestSound').play();
  break;

音效文件用免费 CC0 音效网站下载(如 freesound.org),导出为 MP3,放入 sound/ 目录。注意:移动端需用户手势触发(如点击)才能播放音效,本项目天然满足。

技巧二:显示生长倒计时
在地块下方加文字提示:

<div id="plot1" onclick="handlePlotClick(0)" class="plot">
  <img id="img1" src="image/none.jpg" alt="地块1">
  <div id="timer1" class="timer">空地</div>
</div>

CSS 控制样式:

.timer {
  font-size: 12px;
  text-align: center;
  margin-top: 4px;
  color: #666;
}

JS 中更新:

case 2:
  document.getElementById('timer1').innerText = '生长中...';
  break;
case 4:
  document.getElementById('timer1').innerText = '成熟!点击采摘';
  break;

技巧三:添加简易成就系统
localStorage 记录采摘总数(仅限支持的浏览器):

var harvestCount = localStorage.getItem('harvestCount') || 0;
harvestCount = parseInt(harvestCount) + 1;
localStorage.setItem('harvestCount', harvestCount);
document.getElementById('harvestCounter').innerText = '已收获: ' + harvestCount;

在 HTML 中加显示:

<div id="harvestCounter" style="margin:10px;font-weight:bold;">已收获: 0</div>

注意:localStorage 在 iOS Safari 私密模式下不可用,需用 try...catch 包裹,失败时降级为内存变量。

6. 总结与延伸思考:一个 HTML 文件背后的前端世界观

写完这篇解析,我重新打开 Noname1.html,盯着那三块地看了很久。它没有用上任何前沿技术,没有炫目的粒子动画,甚至没有一行注释,但它用最基础的标签、属性、事件,构建了一个自洽的小世界:土地有状态,作物有生命周期,用户操作有即时反馈,所有规则透明可见。这恰恰是前端开发最本真的模样——用标记语言描述结构,用样式语言描述外观,用脚本语言描述行为,三者缺一不可,又各自纯粹

对初学者,它是一把钥匙:当你亲手把 seed.jpg 换成 rice.jpg,把 2000 改成 3000,看着水稻长得更慢,你就真正理解了“状态”与“时间”的关系;当你为第四块地添加逻辑,调试时 console.log 打印出 fieldState 数组的变化,你就触摸到了“数据驱动视图”的脉搏。这种学习,比刷一百道算法题更接近前端的本质。

对我这样的老手,它是一面镜子:照见我们曾经如何用 document.write 拼接页面,如何为 IE6 的盒模型焦头烂额,如何在没有 npm 的年代手动管理 jQuery 插件。技术在进化,但解决问题的逻辑从未改变——识别状态、定义边界、处理转换、反馈用户。今天用 React 写农场,明天用 WebAssembly 写,内核依然是这个状态机。

最后分享一个个人体会:这个项目最珍贵的不是代码,而是它的克制。它没加登录系统(不需要用户),没加排行榜(不需要后端),没加音效(不是必须),甚至没做响应式(三块地在手机上刚好排成一列)。每一行删减,都是对“最小可行产品”(MVP)的虔诚践行。在这个框架泛滥、工具链臃肿的时代,能静下心来,用 <img>onclick 把一件事做到极致,本身就是一种稀缺能力。

如果你已经看到这里,不妨现在就双击 Noname1.html,种下第一颗种子。不需要目标,不需要产出,就单纯感受一下——当指尖点击,图片切换,时间流逝,收获来临,那种掌控数字世界的朴素喜悦。它微小,但真实;它简单,却完整。而这,正是我们爱上编程的最初理由。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个农场小游戏完全用HTML和图片资源搭建,不依赖任何服务器或框架,直接双击Noname1.html就能运行。页面里集成了翻地、播种、等待生长、采摘四个关键环节,通过切换图片(比如seed.jpg代表种子、bud.jpg代表幼苗、harvest.png代表成熟果实)来模拟作物生命周期。所有逻辑都写在单个HTML文件里,结构清晰,事件响应基于原生JavaScript点击触发,适合前端新手理解状态管理与DOM操作。配套资源包括常用作物图(flower.png、chanzi.jpg)、空地占位图(none.jpg)以及备份文件Noname1.html.bak,方便修改后回滚。image文件夹统一存放所有图片,Thumbs.db是系统自动生成的缩略图缓存,可忽略。整个包轻量简洁,没有多余代码或配置,拿来就能跑,也能轻松添加新作物或调整生长周期。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值