调整图片大小以适应画布

121

我有一个表单,允许用户上传图片。

当图片加载完成后,我们会在canvas上对其进行缩放以减小文件大小,然后将其传回服务器。

为此,我们将图片放置在画布上并在那里进行操作。

以下代码将以320 x 240像素的大小在画布上呈现缩放后的图像:

ctx.drawImage(img, 0, 0, canvas.width, canvas.height)

...其中canvas.width和canvas.height是图像高度和宽度,乘以一个基于原始图像大小的缩放因子。

但当我尝试使用该代码时:

ctx.drawImage(img, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height

在画布上我只能看到图片的一部分,例如左上角。我需要将整张图片按比例缩放以适应画布,即使实际图片大小大于320x240的画布大小。

因此,对于上面的代码,宽度和高度为1142x856,因为那是最终图像大小。我需要保持该尺寸,在提交表单时将其传回服务器,但只希望用户在画布上看到一个较小的视图。

我错过了什么?请问有人可以指点我吗?

5个回答

196

在第二次调用中,您犯了错误,将源大小设置为目标大小。
不管怎样,我打赌您想要缩放图像的相同宽高比,因此您需要计算它:

var hRatio = canvas.width / img.width    ;
var vRatio = canvas.height / img.height  ;
var ratio  = Math.min ( hRatio, vRatio );
ctx.drawImage(img, 0,0, img.width, img.height, 0,0,img.width*ratio, img.height*ratio);

我也猜你想把图片居中,所以代码应该是:

function drawImageScaled(img, ctx) {
   var canvas = ctx.canvas ;
   var hRatio = canvas.width  / img.width    ;
   var vRatio =  canvas.height / img.height  ;
   var ratio  = Math.min ( hRatio, vRatio );
   var centerShift_x = ( canvas.width - img.width*ratio ) / 2;
   var centerShift_y = ( canvas.height - img.height*ratio ) / 2;  
   ctx.clearRect(0,0,canvas.width, canvas.height);
   ctx.drawImage(img, 0,0, img.width, img.height,
                      centerShift_x,centerShift_y,img.width*ratio, img.height*ratio);  
}

您可以在此jsbin链接中查看:http://jsbin.com/funewofu/1/edit?js,output


13
谢谢!我把 Math.min() 改成了 Math.max(),以便缩放和裁剪图像来适应画布。这非常有帮助! - Daantje
3
当我从小图像生成大图像时,会产生模糊图像。它变得模糊了...有解决方法吗?请。 - saadk
非常感谢@gamealchemist。阅读这篇文章帮助我理解如何获得缩小的图像,如果我自己去摸索,可能需要很长时间才能弄明白。谢谢! - Chris Schmitz
5
@saadk https://knowyourmeme.com/photos/996305-zoom-and-enhance ;) @saadk,https://knowyourmeme.com/photos/996305-zoom-and-enhance ;) - Rob Hogan
https://dev59.com/EFwZ5IYBdhLWcg3wDMm9#58345223提供了有关模糊问题的有用解决方案。 - havlock

185
提供源图像 (img) 大小作为第一个矩形:
ctx.drawImage(img, 0, 0, img.width,    img.height,     // source rectangle
                   0, 0, canvas.width, canvas.height); // destination rectangle

第二个矩形将是目标尺寸(源矩形将被缩放到该尺寸)。

2016/6更新:关于纵横比和定位(类似于CSS的“cover”方法),请查看:
在Canvas中模拟background-size:cover


1
这很完美,+1。快速提醒 - drawImage() 函数缺少闭合括号。这有点影响我的强迫症 ;) - Frits
1
非常适合我的需求。当表单边距改变时,它使我无需调整图像文件的大小/比例。谢谢;) - Zeek2

5

我猜您希望将图像缩小到较小的尺寸,而不会失去其宽高比。我有一个解决方案。

var ratio = image.naturalWidth / image.naturalHeight;
var width = canvas.width;
var height = width / ratio;
ctx.drawImage(image, 0, 0, width, height);

比例将保持不变。画在画布上的图像也将保持相同的比例。如果图像的高度较长,您可以使用 if 循环,将 canvas.width 替换为其他宽度。


2
您可以在调用ctx.drawImage之前调用ctx.scale()
var factor  = Math.min ( canvas.width / img.width, canvas.height / img.height );
ctx.scale(factor, factor);
ctx.drawImage(img, 0, 0);
ctx.scale(1 / factor, 1 / factor);

这应该保持宽高比。


0

HTML:

<div id="root"></div>

JavaScript:

const images = [
    'https://cdn.pixabay.com/photo/2022/07/25/15/18/cat-7344042_960_720.jpg',
    'https://cdn.pixabay.com/photo/2022/06/27/08/37/monk-7287041_960_720.jpg',
    'https://cdn.pixabay.com/photo/2022/07/18/19/57/dog-7330712_960_720.jpg',
    'https://cdn.pixabay.com/photo/2022/05/22/18/25/spain-7214284_960_720.jpg',
];

const root = document.getElementById('root');

const image = new Image();
image.crossOrigin = 'anonumys';
image.src = images[3];

const canvas = document.createElement('canvas');
canvas.classList.add('track');
canvas.width = 600;
canvas.height = 400;
const ctx = canvas.getContext('2d');

const meta = {
    ratio: image.width / image.height,
    width: 0,
    height: 0,
    offsetX: 0,
    offsetY: 0,
};

if (meta.ratio >= 1) {
    meta.width = canvas.width > image.width ? image.width : canvas.width;
    meta.height = meta.width / meta.ratio;
} else {
    meta.height = canvas.height > image.height ? image.height : canvas.height;
    meta.width = meta.height * meta.ratio;
}

meta.offsetX = canvas.width > meta.width ? (canvas.width - meta.width) / 2 : 0;
meta.offsetY = canvas.height > meta.height ? (canvas.height - meta.height) / 2 : 0;

image.addEventListener('load', () => {
    ctx.drawImage(image, meta.offsetX, meta.offsetY, meta.width, meta.height);
    root.append(canvas);
});

console.log(meta);

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接