返回列表 发布新帖

复刻围住小猫小游戏(404页面源码),单机版

695 0
小K网牛逼 发表于 3 小时前 | 查看全部 阅读模式 <

马上注册,结交更多好友,享用更多功能,让你轻松玩转小K网。

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

×
首先,无意冒犯原作者制作的404小游戏,如涉及侵权会立刻下架。其次,这个游戏做得非常成功,我闲暇时总会忍不住玩上一会儿。真的很感谢,能让人在紧张的工作间隙坐下来放松一下。再次,自从DeepSeek发布后,我曾尝试复刻这个小游戏,但效果不理想。后来一时兴起,买了学生认证的Gemini 3,想第一时间重新复刻。经过多次调试,我觉得已经没什么提升空间了,依然远不及原作的水平。所以决定将HTML分享出来,供大家闲时娱乐。再次感谢作者,这游戏真的很好玩!

复刻围住小猫小游戏(404),单机版

复刻围住小猫小游戏(404页面源码),单机版



代码如下:(新建一个文本文档,复制一下代码,然后文本后缀改成HTML即可。)

  1. <!DOCTYPE html><html lang="zh-CN">
  2. <head>
  3.     <meta charset="UTF-8">
  4.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5.     <title>404 - 围住小猫 (原版魔性跑动复刻)</title>
  6.     <style>
  7.         body { margin: 0; background-color: #f7f8fa; display: flex; flex-direction: column; align-items: center; font-family: "Microsoft YaHei", sans-serif; padding-top: 40px; }
  8.         .container { background: white; padding: 40px 50px; border-radius: 15px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); text-align: center; width: 600px; }
  9.         h1 { font-size: 72px; margin: 0; color: #333; font-weight: bold; }
  10.         .msg-top { color: #666; font-size: 16px; margin: 10px 0; }
  11.         .status-area { height: 60px; display: flex; flex-direction: column; align-items: center; justify-content: center; margin-bottom: 10px; }
  12.         .status-text { font-size: 18px; color: #1890ff; font-weight: bold; }
  13.         .status-text.win { color: #52c41a; }
  14.         .status-text.lose { color: #f5222d; }
  15.         .restart-hint { font-size: 13px; color: #999; margin-top: 5px; opacity: 0; transition: 0.3s; }
  16.         .restart-hint.show { opacity: 1; }
  17.         .canvas-box { padding: 5px; display: inline-block; overflow: visible; }
  18.         canvas { display: block; cursor: pointer; -webkit-tap-highlight-color: transparent; }
  19.         .link-row { margin-top: 25px; display: flex; justify-content: space-between; align-items: center; width: 100%; font-size: 13px; color: #666; }
  20.         .reset-text { cursor: pointer; color: #333; }
  21.         .site-url { color: #999; text-decoration: none; }
  22.         .button-group { margin-top: 35px; display: flex; justify-content: center; gap: 20px; }
  23.         .pill-btn { padding: 10px 45px; border-radius: 25px; border: none; font-size: 15px; cursor: pointer; transition: 0.3s; font-weight: bold; text-decoration: none; }
  24.         .btn-blue { background: #1890ff; color: white; }
  25.         .btn-blue:hover { background: #40a9ff; }
  26.         .btn-grey { background: #f0f2f5; color: #666; }
  27.     </style>
  28. </head>
  29. <body>
  30.   
  31. <div class="container">
  32.     <h1>404</h1>
  33.     <div class="msg-top">您所访问的页面不存在或者已删除</div>
  34.     <div class="status-area">
  35.         <div id="status" class="status-text">点击小圆点,围住小猫</div>
  36.         <div id="restartHint" class="restart-hint">点击游戏区域重开一局</div>
  37.     </div>
  38.     <div class="canvas-box">
  39.         <canvas id="catCanvas"></canvas>
  40.     </div>
  41.     <div class="link-row">
  42.         <span class="reset-text">重置</span>
  43.         <a href="https://www.xkwo.com" class="site-url">www.xkwo.com</a>
  44.     </div>
  45.     <div class="button-group">
  46.         <a href="#" class="pill-btn btn-blue">返回首页</a>
  47.         <a href="#" class="pill-btn btn-grey">查看版规</a>
  48.     </div>
  49. </div>
  50.   
  51. <script>
  52.     const canvas = document.getElementById('catCanvas');
  53.     const ctx = canvas.getContext('2d');
  54.     const statusText = document.getElementById('status');
  55.     const restartHint = document.getElementById('restartHint');
  56.   
  57.     const ROWS = 11, COLS = 11, RADIUS = 18, GAP = 8;
  58.     canvas.width = COLS * (RADIUS * 2 + GAP) + RADIUS + 15;
  59.     canvas.height = ROWS * (RADIUS * 2 + 4) + 15;
  60.   
  61.     let map = [], catPos = { r: 5, c: 5 }, catDrawPos = { x: 0, y: 0 }, catDir = 1;
  62.     let isOver = false, isAnimating = false;
  63.     let aniId = null;
  64.   
  65.     function initMap() {
  66.         if (aniId) cancelAnimationFrame(aniId);
  67.         isOver = false; isAnimating = false;
  68.         statusText.innerText = "点击小圆点,围住小猫";
  69.         statusText.className = "status-text";
  70.         restartHint.className = "restart-hint";
  71.         map = Array.from({ length: ROWS }, () => Array(COLS).fill(0));
  72.         let count = 12 + Math.floor(Math.random() * 6);
  73.         while (count > 0) {
  74.             let r = Math.floor(Math.random() * ROWS), c = Math.floor(Math.random() * COLS);
  75.             if (map[r][c] === 0 && !(r === 5 && c === 5)) { map[r][c] = 1; count--; }
  76.         }
  77.         catPos = { r: 5, c: 5 };
  78.         catDrawPos = getXY(catPos.r, catPos.c);
  79.         startLoop();
  80.     }
  81.   
  82.     function getXY(r, c) {
  83.         const x = RADIUS + 8 + c * (RADIUS * 2 + GAP) + (r % 2 === 1 ? RADIUS : 0);
  84.         const y = RADIUS + 8 + r * (RADIUS * 2 + 4);
  85.         return { x, y };
  86.     }
  87.   
  88.     // 【核心重构】绘制原版风格的橘猫,并实现魔性弹跳跑
  89.     function drawCat(x, y, dir, isRunning) {
  90.         if (isNaN(x) || isNaN(y)) return;
  91.         ctx.save();
  92.          
  93.         // 1. 计算弹跳幅度
  94.         const t = Date.now();
  95.         let bobY = 0;
  96.         if (isRunning) {
  97.             // 跑动时:快速、大幅度的上下颠簸 (魔性核心!)
  98.             bobY = Math.sin(t / 30) * 3.5;
  99.         } else {
  100.             // 静止时:非常缓慢的呼吸感
  101.             bobY = Math.sin(t / 400) * 0.8;
  102.         }
  103.   
  104.         // 应用位置和弹跳
  105.         ctx.translate(x, y + bobY);
  106.         ctx.scale(dir, 1); // 水平翻转
  107.   
  108.         // --- 开始绘制卡通橘猫 Sprite ---
  109.         const furColor = "#FF8C00"; // 橘色
  110.         const bellyColor = "#FFB347"; // 浅橘色肚子
  111.          
  112.         // 身体 (圆润的矩形)
  113.         ctx.fillStyle = furColor;
  114.         ctx.beginPath();
  115.         ctx.roundRect(-14, -8, 28, 16, 8);
  116.         ctx.fill();
  117.         // 肚子 (浅色斑纹)
  118.         ctx.fillStyle = bellyColor;
  119.         ctx.beginPath();
  120.         ctx.ellipse(0, 2, 10, 5, 0, 0, Math.PI*2);
  121.         ctx.fill();
  122.   
  123.         // 头 (圆形)
  124.         ctx.fillStyle = furColor;
  125.         ctx.beginPath();
  126.         ctx.arc(12, -6, 11, 0, Math.PI * 2);
  127.         ctx.fill();
  128.   
  129.         // 耳朵 (三角形)
  130.         ctx.beginPath(); ctx.moveTo(8, -14); ctx.lineTo(16, -14); ctx.lineTo(12, -24); ctx.fill();
  131.         ctx.beginPath(); ctx.moveTo(14, -12); ctx.lineTo(22, -10); ctx.lineTo(19, -20); ctx.fill();
  132.   
  133.         // 眼睛 (眼白和眼珠)
  134.         ctx.fillStyle = "#FFF";
  135.         ctx.beginPath(); ctx.arc(16, -7, 2.5, 0, Math.PI*2); ctx.fill();
  136.         ctx.fillStyle = "#000";
  137.         ctx.beginPath(); ctx.arc(17, -7, 1.2, 0, Math.PI*2); ctx.fill();
  138.   
  139.         // 腿 (简单的圆柱体,固定在身体下方,随身体一起弹跳)
  140.         ctx.fillStyle = furColor;
  141.         // 前腿
  142.         ctx.beginPath(); ctx.roundRect(8, 5, 5, 10, 2.5); ctx.fill();
  143.         // 后腿
  144.         ctx.beginPath(); ctx.roundRect(-12, 5, 5, 10, 2.5); ctx.fill();
  145.   
  146.         // 尾巴
  147.         ctx.lineWidth = 4; ctx.strokeStyle = furColor; ctx.lineCap = "round";
  148.         ctx.beginPath(); ctx.moveTo(-14, -2); ctx.quadraticCurveTo(-24, -8, -22, -18); ctx.stroke();
  149.   
  150.         ctx.restore();
  151.     }
  152.   
  153.     function startLoop() {
  154.         const loop = () => {
  155.             ctx.clearRect(0, 0, canvas.width, canvas.height);
  156.             for (let r = 0; r < ROWS; r++) {
  157.                 for (let c = 0; c < COLS; c++) {
  158.                     const { x, y } = getXY(r, c);
  159.                     ctx.beginPath(); ctx.arc(x, y, RADIUS, 0, Math.PI * 2);
  160.                     ctx.fillStyle = map[r][c] === 1 ? "#34495e" : "#b3d9ff"; ctx.fill();
  161.                 }
  162.             }
  163.             const catBase = getXY(catPos.r, catPos.c);
  164.             if (!isAnimating || (isAnimating && !isOver)) {
  165.                 ctx.beginPath(); ctx.arc(catBase.x, catBase.y, RADIUS, 0, Math.PI*2);
  166.                 ctx.fillStyle = "#b3d9ff"; ctx.fill();
  167.             }
  168.             // 持续传递 isRunning 状态给绘图函数
  169.             drawCat(catDrawPos.x, catDrawPos.y, catDir, isAnimating && !isOver);
  170.             aniId = requestAnimationFrame(loop);
  171.         };
  172.         loop();
  173.     }
  174.   
  175.     function getAdj(r, c) {
  176.         let n = [[r, c-1], [r, c+1]];
  177.         if (r % 2 === 0) n.push([r-1, c-1], [r-1, c], [r+1, c-1], [r+1, c]);
  178.         else n.push([r-1, c], [r-1, c+1], [r+1, c], [r+1, c+1]);
  179.         return n.filter(p => p[0]>=0 && p[0]<ROWS && p[1]>=0 && p[1]<COLS);
  180.     }
  181.   
  182.     function findPath(startR, startC) {
  183.         let queue = [{ r: startR, c: startC, path: [] }], visited = new Set([`${startR},${startC}`]);
  184.         while (queue.length > 0) {
  185.             let curr = queue.shift();
  186.             if (curr.r === 0 || curr.r === ROWS - 1 || curr.c === 0 || curr.c === COLS - 1) return curr.path;
  187.             for (let [nr, nc] of getAdj(curr.r, curr.c)) {
  188.                 if (map[nr][nc] === 0 && !visited.has(`${nr},${nc}`)) {
  189.                     visited.add(`${nr},${nc}`);
  190.                     queue.push({ r: nr, c: nc, path: curr.path.concat({ r: nr, c: nc }) });
  191.                 }
  192.             }
  193.         }
  194.         return null;
  195.     }
  196.   
  197.     function catMove() {
  198.         if (isOver || isAnimating) return;
  199.         let path = findPath(catPos.r, catPos.c);
  200.         if (!path) {
  201.             isOver = true; statusText.innerText = "猫已经无路可走,你赢了";
  202.             statusText.className = "status-text win"; restartHint.className = "restart-hint show";
  203.             return;
  204.         }
  205.         isAnimating = true;
  206.         const next = path[0], startXY = { ...catDrawPos }, endXY = getXY(next.r, next.c);
  207.         catDir = endXY.x < startXY.x ? -1 : 1;
  208.         catPos = next;
  209.          
  210.         let startTime = null;
  211.         const duration = 180; // 跑动速度
  212.         const aniStep = (time) => {
  213.             if (!startTime) startTime = time;
  214.             const p = Math.min((time - startTime) / duration, 1);
  215.             catDrawPos.x = startXY.x + (endXY.x - startXY.x) * p;
  216.             catDrawPos.y = startXY.y + (endXY.y - startXY.y) * p;
  217.             if (p < 1) requestAnimationFrame(aniStep);
  218.             else {
  219.                 isAnimating = false;
  220.                 if (catPos.r === 0 || catPos.r === ROWS-1 || catPos.c === 0 || catPos.c === COLS-1) {
  221.                     isOver = true; statusText.innerText = "猫已经跑到地图边缘了,你输了";
  222.                     statusText.className = "status-text lose"; restartHint.className = "restart-hint show";
  223.                 }
  224.             }
  225.         };
  226.         requestAnimationFrame(aniStep);
  227.     }
  228.   
  229.     canvas.addEventListener('click', (e) => {
  230.         if (isOver) { initMap(); return; }
  231.         if (isAnimating) return;
  232.         const rect = canvas.getBoundingClientRect();
  233.         const mX = e.clientX - rect.left, mY = e.clientY - rect.top;
  234.         for (let r = 0; r < ROWS; r++) {
  235.             for (let c = 0; c < COLS; c++) {
  236.                 const { x, y } = getXY(r, c);
  237.                 if (Math.hypot(mX - x, mY - y) < RADIUS - 2) {
  238.                     if (map[r][c] === 0 && !(r === catPos.r && c === catPos.c)) {
  239.                         statusText.innerText = `您点击了 (${r}, ${c})`;
  240.                         map[r][c] = 1; catMove();
  241.                     }
  242.                 }
  243.             }
  244.         }
  245.     });
  246.   
  247.     initMap();
  248. </script>
  249. </body>
  250. </html>
复制代码


回复

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

您需要 登录 后才可以回复,轻松玩转社区,没有帐号?立即注册
快速回复
关灯 在本版发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表