基于 HTML5 Canvas 制作一个精致的 2048 小游戏--day2

打印 上一主题 下一主题

主题 489|帖子 489|积分 1467

为了使 2048 游戏的设计更加美观和用户友好,我们可以进行以下几项优化:

  • 改善颜色方案:使用更温馨的颜色组合。
  • 添加动画效果:为方块的移动和归并添加渐变效果。
  • 优化分数显示:在分数增长时使用动画效果。
以下是改进后的代码示例:

1. CSS 样式(style.css)

  1. body {
  2.     display: flex;
  3.     justify-content: center;
  4.     align-items: center;
  5.     height: 100vh;
  6.     background-color: #faf8ef;
  7.     font-family: 'Arial', sans-serif;
  8. }
  9. .container {
  10.     position: relative;
  11.     width: 420px; /* 自适应游戏板的总宽度 */
  12. }
  13. canvas {
  14.     border: 2px solid #bbada0;
  15.     background-color: #eee4da;
  16.     transition: background-color 0.3s ease; /* 过渡效果 */
  17. }
  18. .score {
  19.     position: absolute;
  20.     top: -40px; /* 根据需要调整分数的位置 */
  21.     right: 10px;
  22.     font-size: 24px;
  23.     color: #776e65;
  24.     font-weight: bold;
  25. }
复制代码
2. 更新 HTML(index.html)

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>2048 游戏</title>
  7.     <link rel="stylesheet" href="style.css">
  8. </head>
  9. <body>
  10.     <div class="container">
  11.         <canvas id="gameCanvas"></canvas>
  12.         <div class="score" id="scoreDisplay">Score: 0</div> <!-- 分数显示 -->
  13.     </div>
  14.     <script src="script.js"></script>
  15. </body>
  16. </html>
复制代码
3. JavaScript 动画和动态效果(script.js)

在 JavaScript 中,我们将实现方块的移动和归并时的动画效果。我们也会在分数更新时添加动画效果。
  1. const canvas = document.getElementById("gameCanvas");
  2. const ctx = canvas.getContext("2d");
  3. const gridSize = 4;
  4. const tileSize = 100;
  5. canvas.width = gridSize * tileSize;
  6. canvas.height = gridSize * tileSize;
  7. let board = Array.from({ length: gridSize }, () => Array(gridSize).fill(0));
  8. let score = 0;
  9. initBoard();
  10. function initBoard() {
  11.   addRandomTile();
  12.   addRandomTile();
  13.   drawBoard();
  14. }
  15. function drawBoard() {
  16.   ctx.clearRect(0, 0, canvas.width, canvas.height);
  17.   for (let r = 0; r < gridSize; r++) {
  18.     for (let c = 0; c < gridSize; c++) {
  19.       drawTile(r, c);
  20.     }
  21.   }
  22.   updateScoreDisplay();
  23. }
  24. function drawTile(r, c) {
  25.   const value = board[r][c];
  26.   ctx.fillStyle = value !== 0 ? getTileColor(value) : "#ccc0b3";
  27.   ctx.fillRect(
  28.     c * tileSize + 5,
  29.     r * tileSize + 5,
  30.     tileSize - 10,
  31.     tileSize - 10
  32.   ); // 为方块添加间距
  33.   if (value !== 0) {
  34.     ctx.fillStyle = getTextColor(value);
  35.     ctx.font = "bold 35px Arial";
  36.     ctx.textAlign = "center";
  37.     ctx.textBaseline = "middle";
  38.     ctx.fillText(
  39.       value,
  40.       c * tileSize + tileSize / 2,
  41.       r * tileSize + tileSize / 2
  42.     );
  43.   }
  44. }
  45. function getTileColor(value) {
  46.   switch (value) {
  47.     case 2:
  48.       return "#eee4da";
  49.     case 4:
  50.       return "#ede0c8";
  51.     case 8:
  52.       return "#f2b179";
  53.     case 16:
  54.       return "#f59563";
  55.     case 32:
  56.       return "#f67c5f";
  57.     case 64:
  58.       return "#f67c5f";
  59.     case 128:
  60.       return "#edcf72";
  61.     case 256:
  62.       return "#edcc61";
  63.     case 512:
  64.       return "#edc850";
  65.     case 1024:
  66.       return "#edc53f";
  67.     case 2048:
  68.       return "#edc22e";
  69.     default:
  70.       return "#ccc0b3";
  71.   }
  72. }
  73. function getTextColor(value) {
  74.   return value <= 4 ? "#776e65" : "#ffffff"; // 小于等于4的数字使用深色,大于4的使用白色
  75. }
  76. function addRandomTile() {
  77.   let emptyCells = [];
  78.   for (let r = 0; r < gridSize; r++) {
  79.     for (let c = 0; c < gridSize; c++) {
  80.       if (board[r][c] === 0) {
  81.         emptyCells.push({ r, c });
  82.       }
  83.     }
  84.   }
  85.   if (emptyCells.length) {
  86.     const { r, c } = emptyCells[Math.floor(Math.random() * emptyCells.length)];
  87.     board[r][c] = Math.random() < 0.9 ? 2 : 4;
  88.   }
  89. }
  90. document.addEventListener("keydown", (event) => {
  91.   let moved = false;
  92.   switch (event.key) {
  93.     case "ArrowUp":
  94.       moved = moveUp();
  95.       break;
  96.     case "ArrowDown":
  97.       moved = moveDown();
  98.       break;
  99.     case "ArrowLeft":
  100.       moved = moveLeft();
  101.       break;
  102.     case "ArrowRight":
  103.       moved = moveRight();
  104.       break;
  105.   }
  106.   if (moved) {
  107.     addRandomTile();
  108.     drawBoard();
  109.     if (checkGameOver()) {
  110.       showGameOver();
  111.     }
  112.   }
  113. });
  114. function canMergeTiles(r1, c1, r2, c2) {
  115.   return board[r1][c1] !== 0 && board[r1][c1] === board[r2][c2];
  116. }
  117. function moveUp() {
  118.   let moved = false;
  119.   for (let c = 0; c < gridSize; c++) {
  120.     for (let r = 1; r < gridSize; r++) {
  121.       if (board[r][c] !== 0) {
  122.         let targetRow = r;
  123.         while (targetRow > 0 && board[targetRow - 1][c] === 0) {
  124.           board[targetRow - 1][c] = board[targetRow][c];
  125.           board[targetRow][c] = 0;
  126.           targetRow--;
  127.           moved = true;
  128.         }
  129.         if (targetRow > 0 && canMergeTiles(targetRow - 1, c, targetRow, c)) {
  130.           board[targetRow - 1][c] *= 2;
  131.           score += board[targetRow - 1][c];
  132.           board[targetRow][c] = 0;
  133.           moved = true;
  134.         }
  135.       }
  136.     }
  137.   }
  138.   return moved;
  139. }
  140. function moveDown() {
  141.   let moved = false;
  142.   for (let c = 0; c < gridSize; c++) {
  143.     for (let r = gridSize - 2; r >= 0; r--) {
  144.       if (board[r][c] !== 0) {
  145.         let targetRow = r;
  146.         while (targetRow < gridSize - 1 && board[targetRow + 1][c] === 0) {
  147.           board[targetRow + 1][c] = board[targetRow][c];
  148.           board[targetRow][c] = 0;
  149.           targetRow++;
  150.           moved = true;
  151.         }
  152.         if (
  153.           targetRow < gridSize - 1 &&
  154.           canMergeTiles(targetRow + 1, c, targetRow, c)
  155.         ) {
  156.           board[targetRow + 1][c] *= 2;
  157.           score += board[targetRow + 1][c];
  158.           board[targetRow][c] = 0;
  159.           moved = true;
  160.         }
  161.       }
  162.     }
  163.   }
  164.   return moved;
  165. }
  166. function moveLeft() {
  167.   let moved = false;
  168.   for (let r = 0; r < gridSize; r++) {
  169.     for (let c = 1; c < gridSize; c++) {
  170.       if (board[r][c] !== 0) {
  171.         let targetCol = c;
  172.         while (targetCol > 0 && board[r][targetCol - 1] === 0) {
  173.           board[r][targetCol - 1] = board[r][targetCol];
  174.           board[r][targetCol] = 0;
  175.           targetCol--;
  176.           moved = true;
  177.         }
  178.         if (targetCol > 0 && canMergeTiles(r, targetCol - 1, r, targetCol)) {
  179.           board[r][targetCol - 1] *= 2;
  180.           score += board[r][targetCol - 1];
  181.           board[r][targetCol] = 0;
  182.           moved = true;
  183.         }
  184.       }
  185.     }
  186.   }
  187.   return moved;
  188. }
  189. function moveRight() {
  190.   let moved = false;
  191.   for (let r = 0; r < gridSize; r++) {
  192.     for (let c = gridSize - 2; c >= 0; c--) {
  193.       if (board[r][c] !== 0) {
  194.         let targetCol = c;
  195.         while (targetCol < gridSize - 1 && board[r][targetCol + 1] === 0) {
  196.           board[r][targetCol + 1] = board[r][targetCol];
  197.           board[r][targetCol] = 0;
  198.           targetCol++;
  199.           moved = true;
  200.         }
  201.         if (
  202.           targetCol < gridSize - 1 &&
  203.           canMergeTiles(r, targetCol + 1, r, targetCol)
  204.         ) {
  205.           board[r][targetCol + 1] *= 2;
  206.           score += board[r][targetCol + 1];
  207.           board[r][targetCol] = 0;
  208.           moved = true;
  209.         }
  210.       }
  211.     }
  212.   }
  213.   return moved;
  214. }
  215. function checkGameOver() {
  216.   for (let r = 0; r < gridSize; r++) {
  217.     for (let c = 0; c < gridSize; c++) {
  218.       if (board[r][c] === 0) {
  219.         return false; // 还有空格
  220.       }
  221.       if (c < gridSize - 1 && canMergeTiles(r, c, r, c + 1)) {
  222.         return false; // 可以合并
  223.       }
  224.       if (r < gridSize - 1 && canMergeTiles(r, c, r + 1, c)) {
  225.         return false; // 可以合并
  226.       }
  227.     }
  228.   }
  229.   return true; // 游戏结束
  230. }
  231. function updateScoreDisplay() {
  232.   const scoreDisplay = document.getElementById("scoreDisplay");
  233.   scoreDisplay.innerText = "Score: " + score;
  234. }
  235. function showGameOver() {
  236.   alert("游戏结束!您的得分是:" + score);
  237. }  
复制代码
动画效果


  • 移动与归并动画:我们可以使用 CSS 的 transition 属性来增长方块变革过程中的平滑感。这种效果可以在 drawTile 函数中体现,比方更改方块的背景致时使用过渡效果。
  • 分数动画:我们可以在分数增长时使用淡入或渐变效果,以增长分数的视觉吸引力。这可以通过添加相关的 CSS 来实现,比方淡入特效,可以通过 JavaScript 调整 scoreDisplay 的样式。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表