画布-如何正确旋转垂直/水平图像?

时间:2018-07-14 19:57:29

标签: javascript html5 canvas rotation html5-canvas

我已经看过许多旋转画布/缩放画布解决的问题,但是解决方案并没有真正解决这个问题,因此仍然不确定如何使它在画布上正常工作。

有一个垂直固定尺寸的矩形100w×150h,如下面的红色边框所示。添加图像(垂直/水平/正方形)并旋转后,它应该旋转并在垂直固定尺寸矩形内正确缩放,如下例所示。

在第一个示例中,我们将使用垂直图像(埃菲尔铁塔原始图像在240w×400h处),这是在所有四个旋转角度下的外观:{{3 }}

在第二个示例中,我们将使用水平图像(狗原始图像为1280w×720h),这是在所有四个旋转角度下的外观:

enter image description here

使用画布完成此任务的最有效方法是什么?

(我知道css可用于transform: rotate(90deg)并使用背景大小/位置属性,但是我正在尝试学习如何使用画布完成垂直/水平/正方形图像的上述示例)。

enter image description here

1 个答案:

答案 0 :(得分:2)

我们不需要任何canvas.width/2-image.width/2代码,因此只需使用onload即可将ctx.drawImage(image,0,0, canvas.width, canvas.height)更改为。与此一起,您可以定义一个全局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);
}

现在,旋转图像中心的最佳方法是将图像中心平移到画布的(0,0)点。然后,您可以旋转并将其移回原来的位置。这是因为应用旋转时,画布的(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();
}

Updated Fiddle

更新:

水平图像看起来很奇怪的原因是由于两件事。当前,缩放比例假定在水平图像翻转逻辑的情况下,需要在侧向放大图像。相反,我们想在正常翻转或上下翻转时放大:

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)。这是因为当我们绘制图像时,我们会将其拟合到垂直的画布上,从而导致图像在绘制到画布上时会拉伸。

可以通过更改绘制图像目标位置的位置来解决此问题。对于水平图像,我们要水平绘制:

enter image description here

一个注意事项是对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方法以应用缩放更改。除了xImgOffsetyImgOffset之外,水平和垂直画布大小的起始位置之间的位置差也与原始图像尺寸成比例。

外观上看起来像这样:

enter image description here

在上图中,当我们在画布中绘制水平图像时,我们需要将其绘制为绿色水平矩形。对于垂直图像,将以设置为画布宽度的宽度绘制图像,并以偏移量成比例缩放高度,以使图像居中。同样,水平图像也是如此,我们只需要记住,我们在绘制时就好像画布最初是水平的一样(请参见第一个图形)。

最后,整个方法看起来像这样:

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

此设置可以使用任何画布尺寸和大小。