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:
原始图像
我想从答案中得到一些定义良好的函数,所以最后得到了这些,我希望对其他人也有用,
function getImageFromLink(link) {
return new Promise(function (resolve) {
var image = new Image();
image.onload = function () { resolve(image); };
image.src = link;
});
}
function resizeImageToBlob(image, width, height, mime) {
return new Promise(function (resolve) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
return canvas.toBlob(resolve, mime);
});
}
getImageFromLink(location.href).then(function (image) {
// calculate these based on the original size
var width = image.width / 4;
var height = image.height / 4;
return resizeImageToBlob(image, width, height, 'image/jpeg');
}).then(function (blob) {
// Do something with the result Blob object
document.querySelector('img').src = URL.createObjectURL(blob);
});
为了测试这个,在一个标签中打开的图像上运行它。
我知道这是一个老话题,但它可能对像我这样的人有用,几个月后,我们第一次遇到这个问题。
这里有一些代码,可以在每次重新加载图像时调整图像的大小。我知道这根本不是最优的,但我提供它作为概念的证明。
另外,很抱歉使用jQuery来做简单的选择器,但我觉得语法太舒服了。
$(document).on('ready', createImage);
$(window).on('resize', createImage);
var createImage = function(){
var canvas = document.getElementById('myCanvas');
canvas.width = window.innerWidth || $(window).width();
canvas.height = window.innerHeight || $(window).height();
var ctx = canvas.getContext('2d');
img = new Image();
img.addEventListener('load', function () {
ctx.drawImage(this, 0, 0, w, h);
});
img.src = 'http://www.ruinvalor.com/Telanor/images/original.jpg';
};
html, body{
height: 100%;
width: 100%;
margin: 0;
padding: 0;
background: #000;
}
canvas{
position: absolute;
left: 0;
top: 0;
z-index: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
<meta charset="utf-8" />
<title>Canvas Resize</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
我的createImage函数在文档加载时被调用一次,之后每次窗口接收到调整大小事件时都被调用。
我在Mac电脑上的Chrome 6和Firefox 3.6中进行了测试。这种“技术”对处理器的消耗就像夏天的冰淇淋一样,但它确实奏效了。
我把@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
})
})
其中一些解决方案的问题是,它们直接访问像素数据并通过它进行循环以执行下采样。根据图像的大小,这可能会非常消耗资源,最好使用浏览器的内部算法。
drawImage()函数使用线性插值、最近邻重采样方法。当您没有将大小调整到原来大小的一半以上时,这种方法效果很好。
如果循环一次只调整最大大小的一半,结果会非常好,而且比访问像素数据快得多。
这个函数每次采样到一半,直到达到所需的大小:
function resize_image( src, dst, type, quality ) {
var tmp = new Image(),
canvas, context, cW, cH;
type = type || 'image/jpeg';
quality = quality || 0.92;
cW = src.naturalWidth;
cH = src.naturalHeight;
tmp.src = src.src;
tmp.onload = function() {
canvas = document.createElement( 'canvas' );
cW /= 2;
cH /= 2;
if ( cW < src.width ) cW = src.width;
if ( cH < src.height ) cH = src.height;
canvas.width = cW;
canvas.height = cH;
context = canvas.getContext( '2d' );
context.drawImage( tmp, 0, 0, cW, cH );
dst.src = canvas.toDataURL( type, quality );
if ( cW <= src.width || cH <= src.height )
return;
tmp.src = dst.src;
}
}
// The images sent as parameters can be in the DOM or be image objects
resize_image( $( '#original' )[0], $( '#smaller' )[0] );
本文致谢