使用CSS实现画布的缩放

14

我想在画布上绘制一张图片,然后使用CSS来使画布适应特定的大小。结果发现许多浏览器不能很好地缩小画布。OS X下的Firefox似乎是最糟糕的之一,但我没有测试过很多。以下是问题的最小示例:

HTML

<img>
<canvas></canvas>

CSS

img, canvas {
  width: 125px;
}

JS

var image = document.getElementsByTagName('img')[0],
    canvas = document.getElementsByTagName('canvas')[0];

image.onload = function() {
  canvas.width = image.width;
  canvas.height = image.height;

  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
}

image.src = "http://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Helvetica_Neue_typeface_weights.svg/783px-Helvetica_Neue_typeface_weights.svg.png"

在Codepen中运行:http://codepen.io/ford/pen/GgMzJd

这是在Firefox中的结果(从Retina显示器截图):

canvas_scaling_firefox.png

发生的情况是<img><canvas>都以相同的大小开始,并由浏览器使用CSS进行缩小(图像宽度为783px)。 显然,浏览器对<img>执行了一些漂亮的平滑/插值,但没有对<canvas>执行。

我尝试过:

如何使右侧的图像看起来像左侧的图像? 最好尽可能少的代码(例如,我不想自己实现双三次插值)。


浏览器对HTML 5画布的降采样非常简单,请参见此答案 - Spencer Wieczorek
那个回答是我试图避免的一个完美例子:它是一个非常复杂的黑客,似乎会带来严重的性能影响。 - ford
也许可以使用媒体查询来提供更接近显示尺寸的图像,以便调整大小不那么明显? - markE
4个回答

7
你可以通过将画布的后备存储器按照 window.devicePixelRatio 的值进行缩放来解决像素化问题。不幸的是,目前似乎存在浏览器限制导致图像滤镜效果欠佳,唯一可靠的解决方法是自行实现 自定义解决方案
用以下代码替换当前的 onload:
image.onload = function() {
  var dpr = window.devicePixelRatio;
  canvas.width = image.width * dpr;
  canvas.height = image.height * dpr;

  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
}

结果:

Results

在Windows 8.1上测试了Firefox 35.0.1。请注意,您当前的代码无法处理浏览器缩放事件,这可能会重新引入像素化。您可以通过处理调整大小事件来解决此问题。


4
Canvas并不是用于CSS缩放的:尝试过采样:使用两倍于所需画布大小的画布,并且CSS缩放可以很好地对画布进行降采样。
在高分辨率设备上,您应该再次增加两倍的分辨率,以达到相同的质量。(即使在标准显示器上,4倍也更加出色)。 图像,画布1X、2X和4X(图像,画布1X、2X和4X)

var $ = document.getElementById.bind(document);
var image = $('fntimg');

image.onload = function() {
  drawAllImages();
}

image.src = "http://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Helvetica_Neue_typeface_weights.svg/783px-Helvetica_Neue_typeface_weights.svg.png"

function drawAllImages() {
  drawImage(1);
  drawImage(2);
  drawImage(4);
}

function drawImage(x) {
  console.log('cv' + x + 'X');
  var canvas = $('cv' + x + 'X');
  canvas.width = x * image.width;
  canvas.height = x * image.height;
  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
}
img,
canvas {
  width: 125px;
}
<br>
<img id='fntimg'>
<canvas  id='cv1X'></canvas>
<canvas  id='cv2X'></canvas>
<canvas  id='cv4X'></canvas>
<br>


1
好的,当过采样时,图像看起来有点更好。但要考虑价格。你付出了2倍或4倍画布的代价来购买稍微更好的图像。并且最昂贵的4倍价格可能是在最不需要的移动设备上支付的。在我看来,这里的过采样是一个糟糕的选择,最好创建几个预设大小的图像,并使用媒体查询适当地传递它们。 - markE
1
@markE:我同意,使用适当的软件手动缩小图像将提供更好的结果并减少电池负担。加载图像后,也可以使用正确的代码缩小图像。但是,如果我们听取O.P.的要求:“尽可能少的代码”,我只能看到这个解决方案。 - GameAlchemist

0

0
简单的回答是,你不能这样做。画布就像位图一样,没有更多的功能。
我的想法是: 在缩放时,您应该重新绘制整个表面,并确保将要绘制的图像按比例缩放到画布上。由于它是矢量图形,所以这应该可以实现。但是你肯定要重新绘制画布。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接