哆啦 A 梦

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/icon-min.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="博客-柒书" />
    <title>Canvas 2D-博客-柒书</title>
  </head>
  <body>
    <canvas id="canvasRef" width="280px" height="340px">
      该浏览器暂不支持canvas标签
    </canvas>
  </body>
  <script>
    // 计算角度
    const Deg = (Number) => {
      if (typeof Number === "number") {
        return (Math.PI / 180) * Number;
      }
      return Number;
    };

    const draw = (ctx, x = 0, y = 0) => {
      // 脸轮廓
      ctx.beginPath();
      ctx.fillStyle = "rgb(88,160,232)";
      ctx.arc(140 + x, 100 + y, 90, Deg(0), Deg(360), false);
      ctx.fill();
      ctx.stroke();
      // 项圈
      ctx.beginPath();
      ctx.fillStyle = "rgb(215,42,47)";
      ctx.moveTo(82 + x, 170 + y);
      ctx.quadraticCurveTo(140 + x, 175 + y, 199 + x, 170 + y);
      ctx.quadraticCurveTo(204 + x, 174 + y, 199 + x, 178 + y);
      ctx.quadraticCurveTo(140 + x, 183 + y, 82 + x, 178 + y);
      ctx.quadraticCurveTo(78 + x, 174 + y, 82 + x, 170 + y);
      ctx.fill();
      ctx.stroke();
      // 身体轮廓
      ctx.beginPath();
      ctx.fillStyle = "rgb(88,160,232)";
      ctx.moveTo(90 + x, 178 + y);
      ctx.quadraticCurveTo(65 + x, 155 + y, 40 + x, 140 + y);
      ctx.lineTo(20 + x, 160 + y);
      ctx.quadraticCurveTo(50 + x, 200 + y, 80 + x, 220 + y);
      ctx.quadraticCurveTo(76 + x, 250 + y, 80 + x, 300 + y);
      ctx.quadraticCurveTo(105 + x, 304 + y, 130 + x, 300 + y);
      ctx.lineTo(133 + x, 285 + y);
      ctx.lineTo(150 + x, 275 + y);
      ctx.moveTo(133 + x, 285 + y);
      ctx.lineTo(135 + x, 300 + y);
      ctx.quadraticCurveTo(162 + x, 305 + y, 190 + x, 295 + y);
      ctx.quadraticCurveTo(200 + x, 240 + y, 199 + x, 178 + y);
      ctx.quadraticCurveTo(140 + x, 183 + y, 90 + x, 178 + y);
      ctx.moveTo(199 + x, 178 + y);
      ctx.quadraticCurveTo(215 + x, 190 + y, 230 + x, 220 + y);
      ctx.lineTo(210 + x, 235 + y);
      ctx.quadraticCurveTo(202 + x, 232 + y, 199 + x, 224 + y);
      ctx.fill();
      ctx.stroke();
      // 手
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.arc(24 + x, 144 + y, 17, Deg(0), Deg(360), true);
      ctx.fill();
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(227 + x, 235 + y, 17, Deg(0), Deg(360), true);
      ctx.fill();
      ctx.stroke();
      // 右脚
      ctx.beginPath();
      ctx.moveTo(135 + x, 300 + y);
      ctx.quadraticCurveTo(162 + x, 305 + y, 190 + x, 295 + y);
      ctx.bezierCurveTo(205 + x, 295 + y, 205 + x, 308 + y, 200 + x, 313 + y);
      ctx.quadraticCurveTo(162 + x, 322 + y, 136 + x, 316 + y);
      ctx.fill();
      ctx.stroke();
      // 左脚
      ctx.beginPath();
      ctx.moveTo(80 + x, 300 + y);
      ctx.quadraticCurveTo(105 + x, 304 + y, 130 + x, 300 + y);
      ctx.bezierCurveTo(143 + x, 300 + y, 143 + x, 314 + y, 130 + x, 320 + y);
      ctx.quadraticCurveTo(105 + x, 326 + y, 72 + x, 320 + y);
      ctx.bezierCurveTo(65 + x, 316 + y, 70 + x, 300 + y, 80 + x, 300 + y);
      ctx.fill();
      ctx.stroke();
      // 尾巴
      ctx.beginPath();
      ctx.fillStyle = "rgb(215,42,47)";
      ctx.arc(62 + x, 268 + y, 10, Deg(0), Deg(360), true);
      ctx.moveTo(78 + x, 260 + y);
      ctx.lineTo(70 + x, 265 + y);
      ctx.fill();
      ctx.stroke();
      // 腹部
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.moveTo(115 + x, 180 + y);
      ctx.bezierCurveTo(90 + x, 200 + y, 85 + x, 268 + y, 160 + x, 260 + y);
      ctx.bezierCurveTo(205 + x, 255 + y, 204 + x, 200 + y, 182 + x, 180 + y);
      ctx.quadraticCurveTo(146 + x, 181 + y, 115 + x, 180 + y);
      ctx.fill();
      ctx.stroke();
      // 口袋
      ctx.beginPath();
      ctx.arc(150 + x, 215 + y, 36, Deg(0), Deg(180), false);
      ctx.closePath();
      ctx.stroke();
      // 铃铛
      ctx.beginPath();
      ctx.fillStyle = "rgb(245,215,103)";
      ctx.arc(160 + x, 188 + y, 11, Deg(0), Deg(360), false);
      ctx.fill();
      ctx.stroke();
      ctx.beginPath();
      ctx.moveTo(147 + x, 188 + y);
      ctx.quadraticCurveTo(161 + x, 180 + y, 172 + x, 183 + y);
      ctx.quadraticCurveTo(174 + x, 185 + y, 172 + x, 187 + y);
      ctx.quadraticCurveTo(161 + x, 185 + y, 147 + x, 192 + y);
      ctx.quadraticCurveTo(145 + x, 190 + y, 147 + x, 188 + y);
      ctx.fill();
      ctx.stroke();
      ctx.beginPath();
      ctx.fillStyle = "#000";
      ctx.arc(162 + x, 192 + y, 2.5, Deg(0), Deg(360), false);
      ctx.moveTo(162 + x, 192 + y);
      ctx.lineTo(162 + x, 200 + y);
      ctx.fill();
      ctx.stroke();
      // 脸部轮廓
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.moveTo(90 + x, 170 + y);
      ctx.bezierCurveTo(x - 30, y, 334 + x, y - 4, 200 + x, 170 + y);
      ctx.quadraticCurveTo(140 + x, 175 + y, 90 + x, 170 + y);
      ctx.fill();
      ctx.stroke();
      // 嘴角
      ctx.beginPath();
      ctx.moveTo(205 + x, 110 + y);
      ctx.quadraticCurveTo(220 + x, 110 + y, 230 + x, 90 + y);
      ctx.arc(214 + x, 85 + y, 16, Deg(30), Deg(-160), true);
      ctx.fill();
      ctx.stroke();
      // 嘴
      ctx.beginPath();
      ctx.fillStyle = "rgb(222,43,42)";
      ctx.moveTo(90 + x, 100 + y);
      ctx.quadraticCurveTo(140 + x, 110 + y, 205 + x, 110 + y);
      ctx.bezierCurveTo(190 + x, 180 + y, 100 + x, 180 + y, 85 + x, 105 + y);
      ctx.quadraticCurveTo(82 + x, 98 + y, 90 + x, 100 + y);
      ctx.fill();
      ctx.stroke();
      // 舌头
      ctx.beginPath();
      ctx.fillStyle = "rgb(229,122,30)";
      ctx.moveTo(98 + x, 135 + y);
      ctx.bezierCurveTo(118 + x, 105 + y, 170 + x, 120 + y, 173 + x, 155 + y);
      ctx.bezierCurveTo(155 + x, 168 + y, 108 + x, 160 + y, 98 + x, 135 + y);
      ctx.fill();
      ctx.stroke();
      // 左眼框
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.moveTo(116 + x, 46 + y);
      ctx.bezierCurveTo(116 + x, 76 + y, 158 + x, 76 + y, 152 + x, 42 + y);
      ctx.bezierCurveTo(146 + x, 10 + y, 118 + x, 10 + y, 116 + x, 46 + y);
      ctx.fill();
      ctx.stroke();
      // 右眼框
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.moveTo(152 + x, 42 + y);
      ctx.bezierCurveTo(158 + x, 72 + y, 182 + x, 74 + y, 182 + x, 46 + y);
      ctx.bezierCurveTo(172 + x, 10 + y, 147 + x, 10 + y, 152 + x, 42 + y);
      ctx.fill();
      ctx.stroke();
      // 眼睛
      ctx.beginPath();
      ctx.lineWidth = 3;
      ctx.arc(140 + x, 46 + y, 8, Deg(0), Deg(180), true);
      ctx.moveTo(174 + x, 46 + y);
      ctx.arc(166 + x, 46 + y, 8, Deg(0), Deg(180), true);
      ctx.stroke();
      // 鼻子
      ctx.beginPath();
      ctx.lineWidth = 1;
      ctx.fillStyle = "rgba(218,42,46)";
      ctx.arc(158 + x, 68 + y, 10, Deg(0), Deg(360), true);
      ctx.fill();
      ctx.stroke();
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.arc(156 + x, 66 + y, 3, Deg(0), Deg(360), true);
      ctx.fill();
      // 胡子
      ctx.beginPath();
      ctx.moveTo(158 + x, 78 + y);
      ctx.quadraticCurveTo(160 + x, 93 + y, 160 + x, 108 + y);
      ctx.moveTo(182 + x, 76 + y);
      ctx.lineTo(220 + x, 50 + y);
      ctx.moveTo(184 + x, 86 + y);
      ctx.lineTo(232 + x, 82 + y);
      ctx.moveTo(182 + x, 96 + y);
      ctx.lineTo(226 + x, 105 + y);
      ctx.moveTo(129 + x, 78 + y);
      ctx.lineTo(75 + x, 58 + y);
      ctx.moveTo(127 + x, 88 + y);
      ctx.lineTo(65 + x, 85 + y);
      ctx.moveTo(129 + x, 98 + y);
      ctx.lineTo(75 + x, 115 + y);
      ctx.stroke();
    };

    const canvas = document.querySelector("canvas");
    const ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    draw(ctx, 0, 0);
  </script>
</html>