【问题标题】:Canvas - How to rotate vertical/horizontal image correctly?Canvas - 如何正确旋转垂直/水平图像?
【发布时间】:2018-12-22 21:14:14
【问题描述】:

我查看了许多旋转画布/缩放画布解决的问题,但这些解决方案并没有真正解决这个问题,所以仍然不确定如何让它与画布一起正常工作。

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

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

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

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

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

Here is a fiddle.

【问题讨论】:

    标签: javascript html canvas rotation html5-canvas


    【解决方案1】:

    我们不需要任何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);
    }
    

    现在按中心旋转图像的最佳方法是将图像中心转换为画布的(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 在这种情况下 em>)。

    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 &lt; 1 来确定我们是否会声称图像是水平的。否则它将是垂直的。虽然这在声称垂直与水平方面有点宽泛,但假设我们只有垂直或水平图像,它就可以达到目的。

    即使在进行了这些更改之后,仍然存在一些问题 (see this fiddle)。这是因为当我们绘制图像时,我们会将其拟合到垂直的画布上,导致图像在绘制到画布上时会拉伸。

    这可以通过更改我们绘制图像目标的位置来解决。对于水平图像,我们希望将其水平绘制:

    注意一点是对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 之外,还有与原始图像尺寸成比例的水平和垂直画布尺寸的起始位置之间的位置差异。

    视觉上看起来像这样:

    在上图中,我们需要在画布中绘制水平图像作为绿色水平矩形。对于垂直图像,图像的宽度设置为画布宽度,高度与偏移成比例缩放,因此图像居中。同样,这对于水平图像也是如此,我们只需要记住,我们正在绘制它,就好像画布最初是水平的(参见第一张图)。

    最后整个方法是这样的:

    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

    此设置适用于任何画布尺寸和大小。

    【讨论】:

    • 谢谢!这在垂直图像上效果很好,但我在水平图像上不起作用(用示例图像更新问题)。这是使用示例水平图像更新的小提琴:jsfiddle.net/6ZsCz/6269 但图像与示例比例不匹配,只有垂直图像可以正常工作。我注释掉了ctx.scale(ratio, ratio),但无法让它工作。我错过了什么?
    • @Wonka 那是因为我假设图像是垂直的。我的网络刚刚断了,所以我不能马上更新。但是我们只需要根据图像的比例来检测图像是水平的还是垂直的,然后删除 -90 以另一种方式应用比例。不幸的是,我的手机很难编码,一旦我的互联网恢复,我会更新。
    • 太棒了,谢谢。 Canvas 对我来说是新的,我觉得用 css 做起来要容易得多,但 canvas 完全是另一种野兽。顺便说一句,编程配置文件链接的精彩介绍,现在查看画布,它非常有帮助!干得好:)
    • @Wonka 查看更新后的答案。抱歉耽搁了,我必须弄清楚一个小警告。
    • 非常彻底的答案,这正是我试图用画布和与不同尺寸缩放相关的旋转来完成的。你有很多领域知识,所以非常感谢你分享这些知识并将这个答案放在一起,这对于理解和解释每一步真正发生的事情非常有帮助。关于这个主题的最佳答案之一恕我直言!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    • 2016-07-29
    • 2022-08-03
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多