I'm trying to create a thumbnail image on the client side using javascript and a canvas element, but when I shrink the image down, it looks terrible. It looks as if it was downsized in photoshop with the resampling set to 'Nearest Neighbor' instead of Bicubic. I know its possible to get this to look right, because this site can do it just fine using a canvas as well. I've tried using the same code they do as shown in the "[Source]" link, but it still looks terrible. Is there something I'm missing, some setting that needs to be set or something?

编辑:

我正在调整一张jpg图片的大小。我试过在链接的网站和photoshop中调整相同的jpg图片的大小,缩小后看起来很好。

以下是相关代码:

reader.onloadend = function(e)
{
    var img = new Image();
    var ctx = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");

    img.onload = function()
    {
        var ratio = 1;

        if(img.width > maxWidth)
            ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
            ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
    };

    img.src = reader.result;
}

EDIT2:

看来我错了,链接网站并没有做任何更好的工作缩小图像。我尝试了其他建议的方法,没有一个看起来更好。这就是不同方法的结果:

ps:

帆布:

使用图像渲染:optimizeQuality设置并按宽度/高度缩放:

使用-moz-transform设置和缩放:

Canvas在pixastic上调整大小:

我猜这意味着firefox没有像它应该的那样使用双三次抽样。我只能等他们真的加进去了。

EDIT3:

原始图像


当前回答

如果你只是想调整图像的大小,我建议用CSS设置图像的宽度和高度。这里有一个简单的例子:

.small-image {
    width: 100px;
    height: 100px;
}

注意,高度和宽度也可以使用JavaScript设置。下面是快速代码示例:

var img = document.getElement("my-image");
img.style.width = 100 + "px";  // Make sure you add the "px" to the end,
img.style.height = 100 + "px"; // otherwise you'll confuse IE

此外,为了确保调整大小的图像看起来不错,在图像选择器中添加以下css规则:

-ms-interpolation-mode: bicubic:在IE7中引入 图像渲染:optimizeQuality:在FireFox 3.6中引入

据我所知,除了IE之外,所有浏览器都默认使用双三次算法来调整图像大小,所以你调整后的图像在Firefox和Chrome中应该看起来不错。

如果设置css的宽度和高度不工作,你可能想玩一个css转换:

-moz-transform: scale(sx[, sy]) -webkit-transform:规模(sx (sy))

如果出于任何原因你需要使用画布,请注意有两种方法可以调整图像的大小:通过css调整画布的大小或以较小的尺寸绘制图像。

请参阅这个问题了解更多细节。

其他回答

所以我发现了一些有趣的东西,在使用画布时可能会有所帮助:

要单独调整画布控件的大小,您需要使用height=""和width=""属性(或canvas.width/canvas. properties)。高度元素)。如果你使用CSS来调整画布的大小,它实际上会拉伸(即:调整大小)画布的内容以适应整个画布(而不是简单地增加或减少画布的面积)。

尝试将图像绘制到画布控件中,并将height和width属性设置为图像的大小,然后使用CSS将画布调整为您想要的大小,这是值得一试的。也许这将使用不同的调整大小算法。

还应该注意的是,canvas在不同浏览器(甚至不同浏览器的不同版本)中具有不同的效果。在浏览器中使用的算法和技术可能会随着时间的推移而改变(特别是Firefox 4和Chrome 6很快就会出来,这将非常强调画布渲染性能)。

此外,您可能也想尝试一下SVG,因为它可能也使用了不同的算法。

祝你好运!

这是一个javascript函数改编自@Telanor的代码。当将图像base64作为第一个参数传递给函数时,它将返回调整后图像的base64。maxWidth和maxHeight是可选的。

function thumbnail(base64, maxWidth, maxHeight) {

  // Max size for thumbnail
  if(typeof(maxWidth) === 'undefined') var maxWidth = 500;
  if(typeof(maxHeight) === 'undefined') var maxHeight = 500;

  // Create and initialize two canvas
  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  var canvasCopy = document.createElement("canvas");
  var copyContext = canvasCopy.getContext("2d");

  // Create original image
  var img = new Image();
  img.src = base64;

  // Determine new ratio based on max size
  var ratio = 1;
  if(img.width > maxWidth)
    ratio = maxWidth / img.width;
  else if(img.height > maxHeight)
    ratio = maxHeight / img.height;

  // Draw original image in second canvas
  canvasCopy.width = img.width;
  canvasCopy.height = img.height;
  copyContext.drawImage(img, 0, 0);

  // Copy and resize second canvas to first canvas
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

  return canvas.toDataURL();

}

我把@syockit的答案和降阶方法转换成了一个可重用的Angular服务,有兴趣的朋友可以访问:https://gist.github.com/fisch0920/37bac5e741eaec60e983

我包括了这两种解决方案,因为它们都有各自的优点和缺点。lanczos卷积方法的质量更高,但代价是速度较慢,而逐步降尺度方法产生了合理的抗锯齿结果,而且速度明显更快。

使用示例:

angular.module('demo').controller('ExampleCtrl', function (imageService) {
  // EXAMPLE USAGE
  // NOTE: it's bad practice to access the DOM inside a controller, 
  // but this is just to show the example usage.

  // resize by lanczos-sinc filter
  imageService.resize($('#myimg')[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })

  // resize by stepping down image size in increments of 2x
  imageService.resizeStep($('#myimg')[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })
})

我有一种感觉,我写的模块将产生类似于photoshop的结果,因为它通过平均颜色数据来保存它们,而不是应用算法。它有点慢,但对我来说是最好的,因为它保留了所有的颜色数据。

https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js

它不会选取最近的邻居并删除其他像素,也不会对一组进行抽样并随机取平均值。它取每个源像素输出到目标像素的精确比例。源点的平均像素颜色就是目标点的平均像素颜色,我认为其他公式不是这样的。

如何使用的例子是在底部 https://github.com/danschumann/limby-resize

2018年10月更新:这些天我的例子比其他任何东西都更学术。Webgl几乎是100%,所以你最好用它来调整大小,以产生类似的结果,但更快。我相信PICA.js就是这么做的。- - - - - -

我已经提出了一些算法在html画布像素数组上做图像插值,可能在这里有用:

https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2

这些可以复制/粘贴,并可以在web worker中使用来调整图像大小(或任何其他需要插值的操作-我正在使用它们来修饰图像)。

我没有添加上面的lanczos的东西,所以如果你愿意,可以随意添加作为比较。