调整HTML5画布中图像的大小

    技术2022-07-11  126

    本文翻译自:Resizing an image in an HTML5 canvas

    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. 我正在尝试使用javascript和canvas元素在客户端创建缩略图,但是当我缩小图像时,它看起来很糟糕。 It looks as if it was downsized in photoshop with the resampling set to 'Nearest Neighbor' instead of Bicubic. 看起来好像是在Photoshop中缩小了尺寸,将重采样设置为“最近的邻居”而不是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. 我尝试使用与[[Source]]链接中所示相同的代码,但是它看起来仍然很糟糕。 Is there something I'm missing, some setting that needs to be set or something? 是否有我所缺少的东西,需要设置的设置或其他东西?

    EDIT: 编辑:

    I'm trying to resize a jpg. 我正在尝试调整jpg的大小。 I have tried resizing the same jpg on the linked site and in photoshop, and it looks fine when downsized. 我尝试在链接的网站和photoshop中调整相同jpg的大小,缩小尺寸后看起来不错。

    Here is the relevant code: 以下是相关代码:

    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: 编辑2:

    Seems I was mistaken, the linked website wasn't doing any better of a job of downsizing the image. 似乎我弄错了,链接的网站在缩小图像尺寸方面做得更好。 I tried the other methods suggested and none of them look any better. 我尝试了建议的其他方法,但没有一个看起来更好。 This is what the different methods resulted in: 这是不同方法导致的结果:

    Photoshop: Photoshop:

    Canvas: 帆布:

    Image with image-rendering: optimizeQuality set and scaled with width/height: 具有图像渲染的图像:optimizeQuality设置并随宽度/高度缩放:

    Image with image-rendering: optimizeQuality set and scaled with -moz-transform: 具有图像渲染的图像:optimizeQuality设置并使用-moz-transform缩放:

    Canvas resize on pixastic: 画布在像素上调整大小:

    I guess this means firefox isn't using bicubic sampling like its supposed to. 我猜这意味着Firefox并未像预期的那样使用三次三次采样。 I'll just have to wait until they actually add it. 我只需要等待,直到他们实际添加它。

    EDIT3: 编辑3:

    Original Image 原始图片


    #1楼

    参考:https://stackoom.com/question/9fII/调整HTML-画布中图像的大小


    #2楼

    This is a javascript function adapted from @Telanor's code. 这是从@Telanor的代码改编而成的javascript函数。 When passing a image base64 as first argument to the function, it returns the base64 of the resized image. 将图像base64作为第一个参数传递给函数时,它将返回调整后大小的图像的base64。 maxWidth and maxHeight are optional. 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(); }

    #3楼

    Thanks @syockit for an awesome answer. 感谢@syockit提供了一个很棒的答案。 however, I had to reformat a little as follows to make it work. 但是,我必须重新格式化以下内容才能使其正常运行。 Perhaps due to DOM scanning issues: 可能是由于DOM扫描问题:

    $(document).ready(function () { $('img').on("load", clickA); function clickA() { var img = this; var canvas = document.createElement("canvas"); new thumbnailer(canvas, img, 50, 3); document.body.appendChild(canvas); } function thumbnailer(elem, img, sx, lobes) { this.canvas = elem; elem.width = img.width; elem.height = img.height; elem.style.display = "none"; this.ctx = elem.getContext("2d"); this.ctx.drawImage(img, 0, 0); this.img = img; this.src = this.ctx.getImageData(0, 0, img.width, img.height); this.dest = { width: sx, height: Math.round(img.height * sx / img.width) }; this.dest.data = new Array(this.dest.width * this.dest.height * 3); this.lanczos = lanczosCreate(lobes); this.ratio = img.width / sx; this.rcp_ratio = 2 / this.ratio; this.range2 = Math.ceil(this.ratio * lobes / 2); this.cacheLanc = {}; this.center = {}; this.icenter = {}; setTimeout(process1, 0, this, 0); } //returns a function that calculates lanczos weight function lanczosCreate(lobes) { return function (x) { if (x > lobes) return 0; x *= Math.PI; if (Math.abs(x) < 1e-16) return 1 var xx = x / lobes; return Math.sin(x) * Math.sin(xx) / x / xx; } } process1 = function (self, u) { self.center.x = (u + 0.5) * self.ratio; self.icenter.x = Math.floor(self.center.x); for (var v = 0; v < self.dest.height; v++) { self.center.y = (v + 0.5) * self.ratio; self.icenter.y = Math.floor(self.center.y); var a, r, g, b; a = r = g = b = 0; for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) { if (i < 0 || i >= self.src.width) continue; var f_x = Math.floor(1000 * Math.abs(i - self.center.x)); if (!self.cacheLanc[f_x]) self.cacheLanc[f_x] = {}; for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) { if (j < 0 || j >= self.src.height) continue; var f_y = Math.floor(1000 * Math.abs(j - self.center.y)); if (self.cacheLanc[f_x][f_y] == undefined) self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000); weight = self.cacheLanc[f_x][f_y]; if (weight > 0) { var idx = (j * self.src.width + i) * 4; a += weight; r += weight * self.src.data[idx]; g += weight * self.src.data[idx + 1]; b += weight * self.src.data[idx + 2]; } } } var idx = (v * self.dest.width + u) * 3; self.dest.data[idx] = r / a; self.dest.data[idx + 1] = g / a; self.dest.data[idx + 2] = b / a; } if (++u < self.dest.width) setTimeout(process1, 0, self, u); else setTimeout(process2, 0, self); }; process2 = function (self) { self.canvas.width = self.dest.width; self.canvas.height = self.dest.height; self.ctx.drawImage(self.img, 0, 0); self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height); var idx, idx2; for (var i = 0; i < self.dest.width; i++) { for (var j = 0; j < self.dest.height; j++) { idx = (j * self.dest.width + i) * 3; idx2 = (j * self.dest.width + i) * 4; self.src.data[idx2] = self.dest.data[idx]; self.src.data[idx2 + 1] = self.dest.data[idx + 1]; self.src.data[idx2 + 2] = self.dest.data[idx + 2]; } } self.ctx.putImageData(self.src, 0, 0); self.canvas.style.display = "block"; } });

    #4楼

    Fast image resize/resample algorithm using Hermite filter with JavaScript. 使用带有JavaScript的Hermite过滤器的快速图像调整大小/重采样算法。 Support transparency, gives good quality. 支持透明,提供良好的质量。 Preview: 预习:

    Update : version 2.0 added on GitHub (faster, web workers + transferable objects). 更新 :在GitHub上添加了2.0版(更快,网络工作者+可转移对象)。 Finally i got it working! 终于我得到了它的工作!

    Git: https://github.com/viliusle/Hermite-resize Git: https : //github.com/viliusle/Hermite-resize Demo: http://viliusle.github.io/miniPaint/ 演示: http : //viliusle.github.io/miniPaint/

    /** * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version! * * @param {HtmlElement} canvas * @param {int} width * @param {int} height * @param {boolean} resize_canvas if true, canvas will be resized. Optional. */ function resample_single(canvas, width, height, resize_canvas) { var width_source = canvas.width; var height_source = canvas.height; width = Math.round(width); height = Math.round(height); var ratio_w = width_source / width; var ratio_h = height_source / height; var ratio_w_half = Math.ceil(ratio_w / 2); var ratio_h_half = Math.ceil(ratio_h / 2); var ctx = canvas.getContext("2d"); var img = ctx.getImageData(0, 0, width_source, height_source); var img2 = ctx.createImageData(width, height); var data = img.data; var data2 = img2.data; for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { var x2 = (i + j * width) * 4; var weight = 0; var weights = 0; var weights_alpha = 0; var gx_r = 0; var gx_g = 0; var gx_b = 0; var gx_a = 0; var center_y = (j + 0.5) * ratio_h; var yy_start = Math.floor(j * ratio_h); var yy_stop = Math.ceil((j + 1) * ratio_h); for (var yy = yy_start; yy < yy_stop; yy++) { var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; var center_x = (i + 0.5) * ratio_w; var w0 = dy * dy; //pre-calc part of w var xx_start = Math.floor(i * ratio_w); var xx_stop = Math.ceil((i + 1) * ratio_w); for (var xx = xx_start; xx < xx_stop; xx++) { var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; var w = Math.sqrt(w0 + dx * dx); if (w >= 1) { //pixel too far continue; } //hermite filter weight = 2 * w * w * w - 3 * w * w + 1; var pos_x = 4 * (xx + yy * width_source); //alpha gx_a += weight * data[pos_x + 3]; weights_alpha += weight; //colors if (data[pos_x + 3] < 255) weight = weight * data[pos_x + 3] / 250; gx_r += weight * data[pos_x]; gx_g += weight * data[pos_x + 1]; gx_b += weight * data[pos_x + 2]; weights += weight; } } data2[x2] = gx_r / weights; data2[x2 + 1] = gx_g / weights; data2[x2 + 2] = gx_b / weights; data2[x2 + 3] = gx_a / weights_alpha; } } //clear and resize canvas if (resize_canvas === true) { canvas.width = width; canvas.height = height; } else { ctx.clearRect(0, 0, width_source, height_source); } //draw ctx.putImageData(img2, 0, 0); }

    #5楼

    If you're simply trying to resize an image, I'd recommend setting width and height of the image with CSS. 如果您只是想调整图像的大小,建议您使用CSS设置图像的width和height 。 Here's a quick example: 这是一个简单的示例:

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

    Note that the height and width can also be set using JavaScript. 注意, height和width也可以使用JavaScript进行设置。 Here's quick code sample: 这是快速的代码示例:

    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

    Also, to ensure that the resized image looks good, add the following css rules to image selector: 另外,为确保调整后的图像看起来不错,请在图像选择器中添加以下css规则:

    -ms-interpolation-mode: bicubic : introduce in IE7 -ms-interpolation-mode: bicubic :在IE7中引入 image-rendering: optimizeQuality : introduced in FireFox 3.6 image-rendering: optimizeQuality :FireFox 3.6中引入

    As far as I can tell, all browsers except IE using an bicubic algorithm to resize images by default, so your resized images should look good in Firefox and Chrome. 据我所知,默认情况下,除IE以外的所有浏览器均使用双三次算法来调整图像大小,因此,在Firefox和Chrome浏览器中,调整大小后的图像应该看起来不错。

    If setting the css width and height doesn't work, you may want to play with a css transform : 如果设置CSS的width和height不起作用,则可能需要使用CSS transform :

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

    If for whatever reason you need to use a canvas, please note that there are two ways an image can be resize: by resizing the canvas with css or by drawing the image at a smaller size. 如果出于任何原因需要使用画布,请注意可以通过两种方式调整图像的大小:使用CSS调整画布的大小或以较小的尺寸绘制图像。

    See this question for more details. 有关更多详细信息,请参见此问题 。


    #6楼

    i got this image by right clicking the canvas element in firefox and saving as. 我通过右键单击firefox中的canvas元素并将其另存为得到此图像。

    var img = new Image(); img.onload = function () { console.debug(this.width,this.height); var canvas = document.createElement('canvas'), ctx; canvas.width = 188; canvas.height = 150; document.body.appendChild(canvas); ctx = canvas.getContext('2d'); ctx.drawImage(img,0,0,188,150); }; img.src = 'original.jpg';

    so anyway, here is a 'fixed' version of your example: 因此,无论如何,这是示例的“固定”版本:

    var img = new Image(); // added cause it wasnt defined var canvas = document.createElement("canvas"); document.body.appendChild(canvas); var ctx = canvas.getContext("2d"); var canvasCopy = document.createElement("canvas"); // adding it to the body document.body.appendChild(canvasCopy); var copyContext = canvasCopy.getContext("2d"); img.onload = function() { var ratio = 1; // defining cause it wasnt var maxWidth = 188, maxHeight = 150; 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; // the line to change // ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height); // the method signature you are using is for slicing ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height); }; // changed for example img.src = 'original.jpg';
    Processed: 0.010, SQL: 9