Canvas - 如何正确旋转 vertical/horizontal 图片?
Canvas - How to rotate vertical/horizontal image correctly?
我看过许多轮换 canvas/scaling canvas 解决的问题,但解决方案并没有真正解决这个问题,所以仍然不确定如何让它与 canvas.
有一个垂直固定尺寸的矩形100w×150h,如下图红框所示。当添加并旋转图像 (vertical/horizontal/square) 时,它应该在垂直固定尺寸矩形内正确旋转和缩放,如下例所示。
在第一个示例中,我们将使用 垂直 图像(埃菲尔铁塔原始图像在 240w × 400h),这是所有四次旋转时的样子角度:
在第二个示例中,我们将使用 水平 图像(1280w × 720h 的狗原始图像),这是它在所有四个旋转角度下的样子:
使用 canvas 完成此任务的最有效方法是什么?
(我知道 css 可以使用 transform: rotate(90deg)
并使用背景 size/position 属性,但我正在尝试学习如何使用 canvas vertical/horizontal/square 张图片)。
我们不需要任何 canvas.width/2-image.width/2
代码,因此只需使用 ctx.drawImage(image,0,0, canvas.width, canvas.height)
即可将您的 onload
更改为。除此之外,您还可以定义一个全局 ratio
变量,当您向侧面旋转并需要向上缩放时,该变量将用于正确缩放:
var ratio = 1;
image.onload=function(){
canvas.width = 100;
canvas.height = 150;
ratio = canvas.height/canvas.width; // We will use this for scaling the image to fit
ctx.drawImage(image,0,0, canvas.width, canvas.height);
}
现在以图像为中心旋转图像的最佳方法是将图像中心平移到 canvas 的 (0,0)
点。然后你可以旋转并将它移回原来的位置。这是因为当应用旋转时,canvas (0,0)
点就是旋转点。
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2); // Move image center to 0,0
ctx.rotate(degrees*Math.PI/180); // Rotate will go from center
ctx.translate(-canvas.width/2,-canvas.height/2); // Move image back to normal spot
ctx.drawImage(image,0,0, canvas.width, canvas.height)
ctx.restore();
}
使用目前的代码,正常和 180 度图像看起来都不错。但是横向的需要向上缩放,为此添加一些逻辑来检测图像是否向左或向右翻转,然后按 ratio
变量缩放(1.5
在这种情况下 ).
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
if((degrees - 90) % 180 == 0) // Is the image sideways?
ctx.scale(ratio, ratio); // Scale it up to fill the canvas
ctx.translate(-canvas.width/2,-canvas.height/2);
ctx.drawImage(image,0,0, canvas.width, canvas.height)
ctx.restore();
}
更新:
水平图像看起来很奇怪的原因有两点。目前,缩放假定图像在横向时需要放大,如果是水平图像,则逻辑被翻转。相反,我们希望在正常翻转或上下翻转时放大:
function drawRotated(degrees) {
ctx.clearRect(0,0,canvas.width,canvas.height);
...
if(imgRatio < 1) angleToScale += 90
if(angleToScale % 180 == 0)
ctx.scale(ratio, ratio);
ctx.translate(-canvas.width/2,-canvas.height/2);
...
}
此处我们根据 imgRatio < 1
是否声明图像是水平的来确定。否则它将是垂直的。虽然这对于声称垂直与水平图像有点宽泛,但它可以用于假设我们只有垂直或水平图像的目的。
尽管在这些更改之后仍然存在一些问题 (see this fiddle)。这是因为当我们绘制图像时,我们将其拟合到垂直的 canvas,导致图像在绘制到 canvas.
时拉伸
这可以通过更改我们绘制图像目标的位置来解决。对于水平图像,我们希望将其水平绘制:
一个注释是对 onload
方法的一些更改:
var ratio = 0;
var xImgOffset = 0;
var yImgOffset = 0;
image.onload=function(){
canvas.width = 100;
canvas.height = 150;
ratio = canvas.height/canvas.width;
var imgRatio = image.height/image.width;
if(imgRatio < 1) { // Horizonal images set Height then proportionally scale width
var dimDiff = image.height/canvas.width;
image.height = canvas.width; // This keeps in mind that the image
image.width = image.width / dimDiff; // is rotated, which is why width is used
} else { // Verticle images set Height then proportionally scale height
var dimDiff = image.width/canvas.width;
image.width = canvas.width;
image.height = image.height / dimDiff;
}
xImgOffset = -(image.width - canvas.width) / 2;
yImgOffset = -(image.height - canvas.height) / 2;
drawRotated(0);
}
立即调用 drawRotated
方法以应用缩放更改。 xImgOffset
和 yImgOffset
是水平和垂直 canvas 大小的起始位置与原始图像尺寸成比例的位置差异。
视觉上看起来像这样:
在上图中,我们需要在 canvas 中绘制水平图像作为绿色水平矩形。对于垂直图像,绘制图像时宽度设置为 canvas 宽度,高度按偏移量按比例缩放,因此图像居中。同样这对于水平图像也是一样的,我们只需要记住我们正在绘制它就好像 canvas 最初是水平的(参见第一个图 )。
最后整个方法看起来像这样:
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
var angleToScale = degrees - 90;
var imgRatio = image.height/image.width;
if(imgRatio < 1) angleToScale += 90
if(angleToScale % 180 == 0)
ctx.scale(ratio, ratio);
ctx.translate(-canvas.width/2,-canvas.height/2);
ctx.drawImage(image, xImgOffset, yImgOffset, image.width, image.height);
ctx.restore();
}
Updated Fiddle For both horizontal and vertical images with original image ratio and cropping
此设置适用于任何 canvas 维度和大小。
我看过许多轮换 canvas/scaling canvas 解决的问题,但解决方案并没有真正解决这个问题,所以仍然不确定如何让它与 canvas.
有一个垂直固定尺寸的矩形100w×150h,如下图红框所示。当添加并旋转图像 (vertical/horizontal/square) 时,它应该在垂直固定尺寸矩形内正确旋转和缩放,如下例所示。
在第一个示例中,我们将使用 垂直 图像(埃菲尔铁塔原始图像在 240w × 400h),这是所有四次旋转时的样子角度:
在第二个示例中,我们将使用 水平 图像(1280w × 720h 的狗原始图像),这是它在所有四个旋转角度下的样子:
使用 canvas 完成此任务的最有效方法是什么?
(我知道 css 可以使用 transform: rotate(90deg)
并使用背景 size/position 属性,但我正在尝试学习如何使用 canvas vertical/horizontal/square 张图片)。
我们不需要任何 canvas.width/2-image.width/2
代码,因此只需使用 ctx.drawImage(image,0,0, canvas.width, canvas.height)
即可将您的 onload
更改为。除此之外,您还可以定义一个全局 ratio
变量,当您向侧面旋转并需要向上缩放时,该变量将用于正确缩放:
var ratio = 1;
image.onload=function(){
canvas.width = 100;
canvas.height = 150;
ratio = canvas.height/canvas.width; // We will use this for scaling the image to fit
ctx.drawImage(image,0,0, canvas.width, canvas.height);
}
现在以图像为中心旋转图像的最佳方法是将图像中心平移到 canvas 的 (0,0)
点。然后你可以旋转并将它移回原来的位置。这是因为当应用旋转时,canvas (0,0)
点就是旋转点。
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2); // Move image center to 0,0
ctx.rotate(degrees*Math.PI/180); // Rotate will go from center
ctx.translate(-canvas.width/2,-canvas.height/2); // Move image back to normal spot
ctx.drawImage(image,0,0, canvas.width, canvas.height)
ctx.restore();
}
使用目前的代码,正常和 180 度图像看起来都不错。但是横向的需要向上缩放,为此添加一些逻辑来检测图像是否向左或向右翻转,然后按 ratio
变量缩放(1.5
在这种情况下 ).
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
if((degrees - 90) % 180 == 0) // Is the image sideways?
ctx.scale(ratio, ratio); // Scale it up to fill the canvas
ctx.translate(-canvas.width/2,-canvas.height/2);
ctx.drawImage(image,0,0, canvas.width, canvas.height)
ctx.restore();
}
更新:
水平图像看起来很奇怪的原因有两点。目前,缩放假定图像在横向时需要放大,如果是水平图像,则逻辑被翻转。相反,我们希望在正常翻转或上下翻转时放大:
function drawRotated(degrees) {
ctx.clearRect(0,0,canvas.width,canvas.height);
...
if(imgRatio < 1) angleToScale += 90
if(angleToScale % 180 == 0)
ctx.scale(ratio, ratio);
ctx.translate(-canvas.width/2,-canvas.height/2);
...
}
此处我们根据 imgRatio < 1
是否声明图像是水平的来确定。否则它将是垂直的。虽然这对于声称垂直与水平图像有点宽泛,但它可以用于假设我们只有垂直或水平图像的目的。
尽管在这些更改之后仍然存在一些问题 (see this fiddle)。这是因为当我们绘制图像时,我们将其拟合到垂直的 canvas,导致图像在绘制到 canvas.
时拉伸这可以通过更改我们绘制图像目标的位置来解决。对于水平图像,我们希望将其水平绘制:
一个注释是对 onload
方法的一些更改:
var ratio = 0;
var xImgOffset = 0;
var yImgOffset = 0;
image.onload=function(){
canvas.width = 100;
canvas.height = 150;
ratio = canvas.height/canvas.width;
var imgRatio = image.height/image.width;
if(imgRatio < 1) { // Horizonal images set Height then proportionally scale width
var dimDiff = image.height/canvas.width;
image.height = canvas.width; // This keeps in mind that the image
image.width = image.width / dimDiff; // is rotated, which is why width is used
} else { // Verticle images set Height then proportionally scale height
var dimDiff = image.width/canvas.width;
image.width = canvas.width;
image.height = image.height / dimDiff;
}
xImgOffset = -(image.width - canvas.width) / 2;
yImgOffset = -(image.height - canvas.height) / 2;
drawRotated(0);
}
立即调用 drawRotated
方法以应用缩放更改。 xImgOffset
和 yImgOffset
是水平和垂直 canvas 大小的起始位置与原始图像尺寸成比例的位置差异。
视觉上看起来像这样:
在上图中,我们需要在 canvas 中绘制水平图像作为绿色水平矩形。对于垂直图像,绘制图像时宽度设置为 canvas 宽度,高度按偏移量按比例缩放,因此图像居中。同样这对于水平图像也是一样的,我们只需要记住我们正在绘制它就好像 canvas 最初是水平的(参见第一个图 )。
最后整个方法看起来像这样:
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
var angleToScale = degrees - 90;
var imgRatio = image.height/image.width;
if(imgRatio < 1) angleToScale += 90
if(angleToScale % 180 == 0)
ctx.scale(ratio, ratio);
ctx.translate(-canvas.width/2,-canvas.height/2);
ctx.drawImage(image, xImgOffset, yImgOffset, image.width, image.height);
ctx.restore();
}
Updated Fiddle For both horizontal and vertical images with original image ratio and cropping
此设置适用于任何 canvas 维度和大小。