调整 HTML5 画布中图像的大小

发布于 2024-08-22 06:54:27 字数 1936 浏览 4 评论 0原文

我正在尝试使用 javascript 和 canvas 元素在客户端创建缩略图,但是当我缩小图像时,它看起来很糟糕。看起来好像是在 Photoshop 中缩小了尺寸,并将重采样设置为“最近邻居”而不是双三次。我知道有可能让它看起来正确,因为这个网站可以使用画布也可以做得很好。我尝试使用与“[Source]”链接中所示相同的代码,但它看起来仍然很糟糕。我是否缺少某些东西,需要设置某些设置或其他什么?

编辑:

我正在尝试调整 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:

似乎我错了,链接的网站在缩小图像尺寸方面并没有做得更好。我尝试了建议的其他方法,但没有一个看起来更好。这是不同方法的结果:

Photoshop:

alt text

画布:

alt text

带有图像渲染的图像:optimizeQuality 设置并按宽度/高度缩放:

alt text

具有图像渲染的图像:optimizeQuality 设置并使用 -moz-transform 缩放:

alt text

画布调整大小在 pixastic 上:

alt text

我想这意味着 Firefox 没有像它应该的那样使用双三次采样。我只需要等到他们真正添加它。

编辑3:

原始图像

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?

EDIT:

I'm trying to resize a jpg. I have tried resizing the same jpg on the linked site and in photoshop, and it looks fine when downsized.

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:

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:

alt text

Canvas:

alt text

Image with image-rendering: optimizeQuality set and scaled with width/height:

alt text

Image with image-rendering: optimizeQuality set and scaled with -moz-transform:

alt text

Canvas resize on pixastic:

alt text

I guess this means firefox isn't using bicubic sampling like its supposed to. I'll just have to wait until they actually add it.

EDIT3:

Original Image

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

冰雪之触 2024-08-29 06:54:27

那么,如果所有浏览器(实际上,Chrome 5 给了我一个相当不错的浏览器)都无法提供足够好的重采样质量,您该怎么办?然后你自己实现它们!噢,来吧,我们正在进入 Web 3.0 的新时代,兼容 HTML5 的浏览器,超级优化的 JIT javascript 编译器,多核(†)机器,拥有大量内存,你在害怕什么?嘿嘿,javascript里面有java这个词,这样应该可以保证性能吧?看哪,缩略图生成代码:

// 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;
    };
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
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(this.process1, 0, this, 0);
}

thumbnailer.prototype.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(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    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";
};

...用它你可以产生这样的结果!

img717.imageshack.us/img717/8910/lanczos358.png

所以无论如何,这是示例的“固定”版本:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

现在是时候把你最好的浏览器拿出来看看哪一款最不可能让你的客户血压升高了!

嗯,我的讽刺标签在哪里?

(由于代码的许多部分基于Anrieff Gallery Generator,它是否也受 GPL2 保护?我不知道)

实际上由于javascript的限制,不支持多核。

So what do you do if all the browsers (actually, Chrome 5 gave me quite good one) won't give you good enough resampling quality? You implement them yourself then! Oh come on, we're entering the new age of Web 3.0, HTML5 compliant browsers, super optimized JIT javascript compilers, multi-core(†) machines, with tons of memory, what are you afraid of? Hey, there's the word java in javascript, so that should guarantee the performance, right? Behold, the thumbnail generating code:

// 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;
    };
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
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(this.process1, 0, this, 0);
}

thumbnailer.prototype.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(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    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";
};

...with which you can produce results like these!

img717.imageshack.us/img717/8910/lanczos358.png

so anyway, here is a 'fixed' version of your example:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

Now it's time to pit your best browsers out there and see which one will least likely increase your client's blood pressure!

Umm, where's my sarcasm tag?

(since many parts of the code is based on Anrieff Gallery Generator is it also covered under GPL2? I don't know)

actually due to limitation of javascript, multi-core is not supported.

┈┾☆殇 2024-08-29 06:54:27

使用 Hermite 过滤器和 JavaScript 进行快速图像调整大小/重新采样算法。支持透明度,提供良好的质量。预览:

在此处输入图像描述

更新:添加了 2.0 版GitHub(更快,网络工作者+可传输对象)。终于我成功了!

Git:https://github.com/viliusle/Hermite-resize
演示: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);
}

Fast image resize/resample algorithm using Hermite filter with JavaScript. Support transparency, gives good quality. Preview:

enter image description here

Update: version 2.0 added on GitHub (faster, web workers + transferable objects). Finally i got it working!

Git: https://github.com/viliusle/Hermite-resize
Demo: 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);
}
臻嫒无言 2024-08-29 06:54:27

尝试 pica - 这是一个高度优化的缩放器,具有可选的算法。请参阅演示

例如,第一篇文章中的原始图像使用 Lanczos 滤镜和 3px 窗口在 120 毫秒内调整大小,或者使用 Box 滤镜和 0.5 像素窗口在 60 毫秒内调整大小。对于 17mb 的巨大图像(5000x3000px),调整大小在桌面上大约需要 1 秒,在移动设备上大约需要 3 秒。

所有调整大小的原则都在这个线程中得到了很好的描述,并且 Pica 没有添加火箭科学。但它针对现代 JIT-s 进行了很好的优化,并且可以开箱即用(通过 npm 或 Bower)。此外,它在可用时使用网络工作者来避免界面冻结。

我还计划很快添加锐化蒙版支持,因为它在缩小比例后非常有用。

Try pica - that's a highly optimized resizer with selectable algorythms. See demo.

For example, original image from first post is resized in 120ms with Lanczos filter and 3px window or 60ms with Box filter and 0.5px window. For huge 17mb image 5000x3000px resize takes ~1s on desktop and 3s on mobile.

All resize principles were described very well in this thread, and pica does not add rocket science. But it's optimized very well for modern JIT-s, and is ready to use out of box (via npm or bower). Also, it use webworkers when available to avoid interface freezes.

I also plan to add unsharp mask support soon, because it's very useful after downscale.

杀お生予夺 2024-08-29 06:54:27

我知道这是一个旧线程,但对于像我这样的人来说,几个月后第一次遇到这个问题可能会有用。

这是一些在每次重新加载图像时调整图像大小的代码。我知道这根本不是最佳的,但我将其作为概念证明提供。

另外,很抱歉使用 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 中对其进行了测试。这种“技术”吃处理器就像吃夏天的冰淇淋一样,但它确实有效。

I know this is an old thread but it might be useful for some people such as myself that months after are hitting this issue for the first time.

Here is some code that resizes the image every time you reload the image. I am aware this is not optimal at all, but I provide it as a proof of concept.

Also, sorry for using jQuery for simple selectors but I just feel too comfortable with the syntax.

$(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>

My createImage function is called once when the document is loaded and after that it is called every time the window receives a resize event.

I tested it in Chrome 6 and Firefox 3.6, both on the Mac. This "technique" eats processor as it if was ice cream in the summer, but it does the trick.

别念他 2024-08-29 06:54:27

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

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

这些可以是复制/粘贴,并且可以在网络工作人员内部使用来调整图像大小(或任何其他需要插值的操作 - 我目前正在使用它们来删除图像)。

我还没有添加上面的 lanczos 内容,所以如果您愿意,请随意添加它作为比较。

I've put up some algorithms to do image interpolation on html canvas pixel arrays that might be useful here:

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

These can be copy/pasted and can be used inside of web workers to resize images (or any other operation that requires interpolation - I'm using them to defish images at the moment).

I haven't added the lanczos stuff above, so feel free to add that as a comparison if you'd like.

时光暖心i 2024-08-29 06:54:27

这是改编自 @Telanor 代码的 JavaScript 函数。当将图像 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();

}

This is a javascript function adapted from @Telanor's code. When passing a image base64 as first argument to the function, it returns the base64 of the resized image. maxWidth and maxHeight are optional.

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();

}
身边 2024-08-29 06:54:27

我强烈建议您查看 此链接 并确保其设置为 true。

控制图像缩放行为

在 Gecko 1.9.2 (Firefox 3.6
/ 雷鸟 3.1 / 耳廓狐 1.0)

Gecko 1.9.2 引入了
mozImageSmoothingEnabled 属性
画布元素;如果这个布尔值
值为 false,图像不会
缩放时平滑。此属性是
默认为 true。查看明文?

  1. cx.mozImageSmoothingEnabled = false;

I'd highly suggest you check out this link and make sure it is set to true.

Controlling image scaling behavior

Introduced in Gecko 1.9.2 (Firefox 3.6
/ Thunderbird 3.1 / Fennec 1.0)

Gecko 1.9.2 introduced the
mozImageSmoothingEnabled property to
the canvas element; if this Boolean
value is false, images won't be
smoothed when scaled. This property is
true by default. view plainprint?

  1. cx.mozImageSmoothingEnabled = false;
绿光 2024-08-29 06:54:27

如果您只是想调整图像大小,我建议使用 CSS 设置图像的宽度高度。下面是一个简单的示例:

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

请注意,heightwidth 也可以使用 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 规则添加到图像选择器:

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

如果设置 css widthheight 不起作用,您可能需要使用 css transform

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

有关更多详细信息,请参阅此问题

If you're simply trying to resize an image, I'd recommend setting width and height of the image with CSS. Here's a quick example:

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

Note that the height and width can also be set using 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:

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.

If setting the css width and height doesn't work, you may want to play with a css transform:

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.

See this question for more details.

夏末 2024-08-29 06:54:27

我通过右键单击 Firefox 中的画布元素并另存为来获得此图像。

alt text

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';

所以无论如何,这是示例的“固定”版本:

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';

i got this image by right clicking the canvas element in firefox and saving as.

alt text

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';
你好,陌生人 2024-08-29 06:54:27

为了调整图像的宽度小于原始宽度,我使用:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

并且它有效=)。

For resizing to image with width less that original, i use:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

and it works =).

九局 2024-08-29 06:54:27

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

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

它不会采用最近的邻居并丢弃其他像素,或者对一组进行采样并取随机平均值。它采用每个源像素应输出到目标像素的确切比例。源中的平均像素颜色将是目标中的平均像素颜色,我认为这些其他公式不会。

如何使用的示例位于底部
https://github.com/danschumann/limby-resize

2018 年 10 月更新< /strong>:现在我的例子比其他任何东西都更具学术性。 Webgl 几乎是 100%,因此您最好调整大小以产生类似的结果,但速度更快。我相信 PICA.js 就是这么做的。 –

I have a feeling the module I wrote will produce similar results to photoshop, as it preserves color data by averaging them, not applying an algorithm. It's kind of slow, but to me it is the best, because it preserves all the color data.

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

It doesn't take the nearest neighbor and drop other pixels, or sample a group and take a random average. It takes the exact proportion each source pixel should output into the destination pixel. The average pixel color in the source will be the average pixel color in the destination, which these other formulas, I think they will not be.

an example of how to use is at the bottom of
https://github.com/danschumann/limby-resize

UPDATE OCT 2018: These days my example is more academic than anything else. Webgl is pretty much 100%, so you'd be better off resizing with that to produce similar results, but faster. PICA.js does this, I believe. –

国际总奸 2024-08-29 06:54:27

其中一些解决方案的问题在于它们直接访问像素数据并循环遍历它以执行下采样。根据图像的大小,这可能会非常消耗资源,最好使用浏览器的内部算法。

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] );

归功于 这篇文章

The problem with some of this solutions is that they access directly the pixel data and loop through it to perform the downsampling. Depending on the size of the image this can be very resource intensive, and it would be better to use the browser's internal algorithms.

The drawImage() function is using a linear-interpolation, nearest-neighbor resampling method. That works well when you are not resizing down more than half the original size.

If you loop to only resize max one half at a time, the results would be quite good, and much faster than accessing pixel data.

This function downsample to half at a time until reaching the desired size:

  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] );

Credits to this post

梦里泪两行 2024-08-29 06:54:27

因此,我不久前在使用画布时发现了一些有趣的东西,这可能会有所帮助:

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

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

。注意到canvas在不同的浏览器中(甚至不同浏览器的不同版本)有不同的效果,浏览器中使用的算法和技术很可能会随着时间的推移而改变(尤其是Firefox 4和Chrome 6这么快就出来了,这会带来沉重的负担)。强调画布渲染性能)

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

祝您好运!

So something interesting that I found a while ago while working with canvas that might be helpful:

To resize the canvas control on its own, you need to use the height="" and width="" attributes (or canvas.width/canvas.height elements). If you use CSS to resize the canvas, it will actually stretch (i.e.: resize) the content of the canvas to fit the full canvas (rather than simply increasing or decreasing the area of the canvas.

It'd be worth a shot to try drawing the image into a canvas control with the height and width attributes set to the size of the image and then using CSS to resize the canvas to the size you're looking for. Perhaps this would use a different resizing algorithm.

It should also be noted that canvas has different effects in different browsers (and even different versions of different browsers). The algorithms and techniques used in the browsers is likely to change over time (especially with Firefox 4 and Chrome 6 coming out so soon, which will place heavy emphasis on canvas rendering performance).

In addition, you may want to give SVG a shot, too, as it likely uses a different algorithm as well.

Best of luck!

你如我软肋 2024-08-29 06:54:27

快速简单的 Javascript 图像缩放器:

https://github.com/calvintwr/blitz-hermite-resize

const blitz = Blitz.create()

/* Promise */
blitz({
    source: DOM Image/DOM Canvas/jQuery/DataURL/File,
    width: 400,
    height: 600
}).then(output => {
    // handle output
})catch(error => {
    // handle error
})

/* Await */
let resized = await blizt({...})

/* Old school callback */
const blitz = Blitz.create('callback')
blitz({...}, function(output) {
    // run your callback.
})

历史

这确实是经过多轮研究、阅读和尝试之后的结果。

缩放器算法使用@ViliusL 的 Hermite 脚本(Hermite 缩放器确实是最快的,并且提供相当好的输出)。扩展了您需要的功能。

分叉 1 个工作线程来调整大小,这样在调整大小时不会冻结浏览器,这与所有其他 JS 调整器不同。

Fast and simple Javascript image resizer:

https://github.com/calvintwr/blitz-hermite-resize

const blitz = Blitz.create()

/* Promise */
blitz({
    source: DOM Image/DOM Canvas/jQuery/DataURL/File,
    width: 400,
    height: 600
}).then(output => {
    // handle output
})catch(error => {
    // handle error
})

/* Await */
let resized = await blizt({...})

/* Old school callback */
const blitz = Blitz.create('callback')
blitz({...}, function(output) {
    // run your callback.
})

History

This is really after many rounds of research, reading and trying.

The resizer algorithm uses @ViliusL's Hermite script (Hermite resizer is really the fastest and gives reasonably good output). Extended with features you need.

Forks 1 worker to do the resizing so that it doesn't freeze your browser when resizing, unlike all other JS resizers out there.

白日梦 2024-08-29 06:54:27

我将 @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
    })
})

I converted @syockit's answer as well as the step-down approach into a reusable Angular service for anyone who's interested: https://gist.github.com/fisch0920/37bac5e741eaec60e983

I included both solutions because they both have their own pros / cons. The lanczos convolution approach is higher quality at the cost of being slower, whereas the step-wise downscaling approach produces reasonably antialiased results and is significantly faster.

Example usage:

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
    })
})
虫児飞 2024-08-29 06:54:27

感谢@syockit 的精彩回答。但是,我必须按如下方式重新格式化才能使其正常工作。可能是由于 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";
}
});

Thanks @syockit for an awesome answer. however, I had to reformat a little as follows to make it work. Perhaps due to DOM scanning issues:

$(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";
}
});
清欢 2024-08-29 06:54:27

我想从这里的答案中得到一些定义良好的函数,所以最终得到了这些函数,希望对其他人也有用,

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);
});

只是为了测试这个在选项卡中打开的图像上运行它。

I wanted some well defined functions out of answers here so ended up with these which am hoping would be useful for others also,

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);
});

Just for the sake of testing this run it on a image opened in a tab.

娇柔作态 2024-08-29 06:54:27

我刚刚运行了一页并排比较,除非最近发生了一些变化,否则我看不出使用 canvas 与简单 css 相比,没有更好的缩小(缩放)尺寸。我在 FF6 Mac OSX 10.7 上进行了测试。与原版相比仍然稍微柔软。

然而,我确实偶然发现了一些确实产生巨大影响的东西,那就是在支持画布的浏览器中使用图像过滤器。实际上,您可以像在 Photoshop 中一样处理图像,包括模糊、锐化、饱和度、波纹、灰度等。

然后我发现了一个很棒的 jQuery 插件,它使这些滤镜的应用变得轻而易举:
http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation -plugin/428234

我只是在调整图像大小后立即应用锐化滤镜,这应该会给您带来所需的效果。我什至不必使用画布元素。

I just ran a page of side by sides comparisons and unless something has changed recently, I could see no better downsizing (scaling) using canvas vs. simple css. I tested in FF6 Mac OSX 10.7. Still slightly soft vs. the original.

I did however stumble upon something that did make a huge difference and that was using image filters in browsers that support canvas. You can actually manipulate images much like you can in Photoshop with blur, sharpen, saturation, ripple, grayscale, etc.

I then found an awesome jQuery plug-in which makes application of these filters a snap:
http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

I simply apply the sharpen filter right after resizing the image which should give you the desired effect. I didn't even have to use a canvas element.

多像笑话 2024-08-29 06:54:27

正在寻找另一个出色的简单解决方案?

var img=document.createElement('img');
img.src=canvas.toDataURL();
$(img).css("background", backgroundColor);
$(img).width(settings.width);
$(img).height(settings.height);

该解决方案将使用浏览器的调整大小算法! :)

Looking for another great simple solution?

var img=document.createElement('img');
img.src=canvas.toDataURL();
$(img).css("background", backgroundColor);
$(img).width(settings.width);
$(img).height(settings.height);

This solution will use the resize algorith of browser! :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文