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
相关产品推荐
相关产品推荐

