You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript Canvas图像自动缩放禁用及无损缩放方法咨询

解决Canvas中SVG图像自动放大且质量下降的问题

问题根源

你的SVG缺少viewBox属性,导致浏览器将其默认渲染为800x600的视口尺寸(SVG的默认内部坐标系)。当你把SVG序列化为Image对象时,它的实际像素尺寸是800x600,而非你设置的CSS样式width:50px;height:50px。这就是为什么drawImage时图像会被放大,且缩放后质量严重受损——你在缩放一个超大的位图。

解决方案

1. 给SVG添加viewBox属性

viewBox定义了SVG的内部坐标范围,让浏览器知道如何映射SVG图形到指定的CSS尺寸。根据你SVG里的图形坐标(polygon和line的点范围大概是x:10-25,y:10-37),可以设置宽松的范围确保所有元素都被包含:

<svg viewBox="0 0 30 40" style="width:50px;height:50px;">
  <polygon points="10,10 10,14 10.5,17 12,23 14,30 16,34 18,36 22,37 25,37 25,30 24.5,28 24,27 23.5,26 11,10" style="fill:lime;stroke:green;stroke-width:1;" />
  <line x1="10" y1="10" x2="25" y2="37" style="stroke:green;stroke-width:1;" />
</svg>

添加viewBox后,SVG会按你设置的CSS尺寸(50x50)正确渲染,序列化后的Image对象自然尺寸就是50x50,且是基于矢量渲染的清晰位图。

2. 正确使用drawImage匹配游戏元素尺寸

你的蛇身宽度是15,直接指定叶子的渲染尺寸为15x15,和蛇身大小匹配,此时因为Image的原始尺寸是矢量渲染的,缩放后不会失真:

// 替换原来的drawImage行
ctx.drawImage(img, leafPos[0], leafPos[1], 15, 15);

修改后的完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Basic Snake Game</title>
  </head>
  <body>
    <!-- 添加了viewBox属性的SVG -->
    <svg viewBox="0 0 30 40" style="width:50px;height:50px;">
      <polygon points="10,10 10,14 10.5,17 12,23 14,30 16,34 18,36 22,37 25,37 25,30 24.5,28 24,27 23.5,26 11,10" style="fill:lime;stroke:green;stroke-width:1;" />
      <line x1="10" y1="10" x2="25" y2="37" style="stroke:green;stroke-width:1;" />
    </svg>
    <script>
      var html = document.querySelector('body');
      html.innerHTML += '<p></p><br/><input type="button" onclick="heading -= 90" value="Left 90*" style="font-size:20pt;"/><input type="button" onclick="heading += 90" value="Right 90*" style="font-size:20pt;"/><canvas style="width:1000px;height:700px;border:3px solid red;"></canvas>';
      var canvas = document.querySelector('canvas');
      var ctx = canvas.getContext('2d');
      var img = document.querySelector('svg');
      var xml = new XMLSerializer().serializeToString(img);
      img = new Image();
      img.src = 'data:image/svg+xml;base64,' + btoa(xml);
      ctx.fillStyle = 'black';
      ctx.fillRect(0, 0, 2000, 1400);
      var width = 15;
      var speed = 1;
      var heading = 0;
      var pos = [15, 10];
      var leafPos;
      leafPos = [Math.floor(Math.random() * 250), Math.floor(Math.random() * 75)];
      ctx.fillRect(30, 20, width, 10);
      var main = setInterval(function() {
        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, 2000, 1400);
        ctx.fillStyle = '#00ff00'; // 修正颜色值的语法错误
        if(heading == -90) {
          heading = 270;
        } if(heading == 360) {
          heading = 0;
        } if(heading == 0) {
          pos[0] += speed;
        } if(heading == 180) {
          pos[0] -= speed;
        } if(heading == 0 || heading == 180) {
          ctx.fillRect(pos[0], pos[1], width, 5);
        } if(heading == 270) {
          pos[1] -= speed;
        } if(heading == 90) {
          pos[1] += speed;
        } if(heading == 270 || heading == 90) {
          ctx.fillRect(pos[0], pos[1], 5, width);
        }

        if((Math.abs(leafPos[0] - pos[0]) + Math.abs(leafPos[1] - pos[1])) <= 15) {
          speed  += 0.5;
          width ++;
          leafPos = [Math.floor(Math.random() * 250), Math.floor(Math.random() * 75)];
        }
        // 指定渲染尺寸为15x15,匹配蛇身大小
        ctx.drawImage(img, leafPos[0], leafPos[1], 15, 15);
        document.querySelector('p').innerHTML = (speed - 1) *10;
      }, 50);
    </script>
  </body>
</html>

额外修正

你之前的ctx.fillStyle = '00ff00'漏了#号,应该是#00ff00,否则颜色不会生效,已经在代码里修正。

内容的提问来源于stack exchange,提问作者rando

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:55:25