使用仅仅图片URL在Javascript中调整图片大小 - Backbone.Js
使用仅仅图片URL在Javascript中调整图片大小 - Backbone.Js
我正在尝试使用canvas调整一些图片的大小,但是我对如何使它们更加平滑感到困惑。
在Photoshop、浏览器等软件中,它们使用了一些算法(如双三次插值、双线性插值),但我不知道这些算法是否内置在canvas中。
这是我的fiddle: http://jsfiddle.net/EWupT/
var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); canvas.width=300 canvas.height=234 ctx.drawImage(img, 0, 0, 300, 234); document.body.appendChild(canvas);
第一个是普通的调整大小的图片标签,第二个是canvas。注意到canvas的效果没有那么平滑。我怎样才能实现“平滑”效果?
原文中介绍了一个可重用的Angular服务,用于处理图像/画布的高质量调整大小。服务包括两种解决方案,因为它们各自有优点和缺点。lanczos卷积方法质量更高,但速度较慢;而逐步缩小的方法产生合理的抗锯齿结果,速度更快。
文章的问题是:使用JavaScript,只使用图像的URL,如何调整图像大小?
解决方法是通过创建一个可重用的Angular服务来实现。该服务包含两种调整大小的方法,分别是lanczos-sinc滤波器和逐步缩小图像大小。这两种方法都可以通过调用服务中的相应函数来实现,并返回调整大小后的图像。
下面是在控制器中调用服务函数的示例代码:
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 }) })
在示例代码中,控制器通过调用imageService中的resize和resizeStep函数来调整图像大小。两个函数都接受图像元素、目标宽度和目标高度作为参数,并返回一个Promise对象,其中包含调整大小后的图像。
最后,作者更新了他的Github用户名,并提供了新的链接。
原因:问题的原因是在使用JavaScript中的URL调整图像大小时遇到了困难。原始的解决方案没有正确处理图像的最后一步,导致结果不正确。
解决方法:为了解决这个问题,作者编写了自己的代码,并提供了一个性能比较的jsfiddle链接。解决方法的主要步骤如下:
1. 创建一个画布和上下文对象。
2. 创建一个用于缩小图像的临时画布和上下文对象。
3. 设置目标画布的大小。
4. 设置当前缩小比例的初始值。
5. 在临时画布上绘制原始图像,并根据当前缩小比例进行缩小。
6. 使用while循环,不断缩小图像,直到达到目标大小。
7. 在目标画布上绘制缩小后的图像。
这个解决方法被评论称为最被低估的答案,并且被证明是有效的。
需要注意的是,这种方法对于透明图像可能会产生一些不完全覆盖像素的问题。
通过使用下采样可以获得更好的结果。大多数浏览器在调整图像大小时似乎使用线性插值而不是双三次插值。
(更新:规范中已添加一个质量属性,imageSmoothingQuality,目前仅在Chrome中可用。)
除非选择禁用平滑或最近邻,否则浏览器在缩小图像后始终对其进行插值,因为此函数作为低通滤波器以避免混叠。
双线性插值使用2x2像素来进行插值,而双三次插值使用4x4像素,因此通过分步进行操作,可以在使用双线性插值时获得接近双三次插值的结果,如所示的结果图像。
解决方法如下所示:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var img = new Image(); img.onload = function () { // set size proportional to image canvas.height = canvas.width * (img.height / img.width); // step 1 - resize to 50% var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width * 0.5; oc.height = img.height * 0.5; octx.drawImage(img, 0, 0, oc.width, oc.height); // step 2 octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5); // step 3, resize to final size ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, canvas.width, canvas.height); } img.src = "//i.imgur.com/SHo6Fub.jpg";
根据调整大小的幅度,如果差异较小,则可以跳过第2步。
在演示中,您可以看到新的结果现在与图像元素非常相似。
对于图像,通常可以通过设置CSS来覆盖此设置。
您可以将步骤减少到仅1步或不进行任何步骤(对于某些图像,这效果很好)。此外,还可以参考类似的答案,添加了锐化卷积,以便在缩小后使结果图像更清晰。
下面是一个使用了仅一个额外步骤的修改过的fiddle链接。
如果处理不同尺寸的图像,如何判断是否仅使用一个额外步骤?或者默认应为1步。
可以很容易地修改此答案中的代码,根据所需的最大尺寸调整大小,并确定所需的单步转换比例以实现该目标。只需进行一些数学计算,以确定将宽度和高度都调整到所需范围内所需的最大比例。
一些数学计算可以在此处找到:
http://stackoverflow.com/questions/17861447/17862644#17862644
谢谢您的指点!我必须承认我不理解为什么需要重复的0.5调整大小,但是那个帖子解释了原因。为了方便起见,我实现了一个单步调整大小,但是现在可能需要重新考虑,如果上传的照片会出现“颤抖”!
代码是OP代码的延续。如果您打开fiddle,您会看到ctx被定义。我在这里内联它,以避免误解。