javascript - 旋转后,在正确位置绘制图像

标签 javascript jquery html canvas

如您所见,我尝试旋转 Canvas 中的图像: enter image description here https://jsfiddle.net/1a7wpsp8/4/

因为我围绕 [0, image_height] 旋转了图像,图像信息丢失了。 部分头部缺失,Canvas 的高度现在太小,无法显示整个旋转图像。

要解决这个问题我想我必须将旋转原点向右移动更多并增加 Canvas 高度。

我添加了一个方法来获取围绕原点旋转特定 Angular 点:

function rotate(x, y, a) {
 var cos = Math.cos,
    sin = Math.sin,

    a = a * Math.PI / 180, 
    xr =  x * cos(a) - y * sin(a);
    yr =  x * sin(a) + y * cos(a);

 return [xr, yr];
}

于是我尝试计算新的旋转原点,但失败了。

旋转图像的首选方法是什么? 如何在 Canvas 上显示没有红色边框的完整图像?谢谢 https://jsfiddle.net/1a7wpsp8/4/

最佳答案

要旋转图像并缩放它,使图像的边缘绝不会在 Canvas 边界内,您需要计算 Canvas 中心到 Angular 落的距离。

// assuming canvas is the canvas and image is the image.
var dist = Math.sqrt(Math.pow(canvas.width/2,2)+Math.pow(canvas.height/2,2));

现在你需要找到从图像中心到它最近边缘的距离;

var imgDist = Math.min(image.width,image.height)/2

因此,现在我们拥有了获取最小比例所需的信息,以便当在 Canvas 中心绘制时始终适合 Canvas 。

var minScale = dist / imgDist;

现在围绕图像的中心旋转,缩放它并将它定位在 Canvas 的中心

首先计算一个像素的上边缘(X轴)的方向和大小

// ang is the rotation in radians
var dx = Math.cos(ang) * minScale;
var dy = Math.sin(ang) * minScale; 

然后创建变换矩阵,其中前两个数字是 X 轴,后两个是与 X 轴顺时针 90 度 Angular 的 Y 轴,最后两个是 Canvas 像素坐标中的原点, Canvas 原点位于左上角。

// ctx is the canvas 2D context
ctx.setTransform(dx, dy, -dy, dx, canvas.width / 2, canvas.height / 2);

现在绘制图像偏移其高度和宽度的一半。

ctx.drawImage(image,-image.width / 2, - image.height / 2);

最后一件事就是恢复默认转换

ctx.setTransform(1,0,0,1,0,0);

大功告成。

作为函数

// ctx is canvas 2D context
// angle is rotation in radians
// image is the image to draw
function drawToFitRotated(ctx, angle, image){
    var dist = Math.sqrt(Math.pow(ctx.canvas.width /2, 2 ) + Math.pow(ctx.canvas.height / 2, 2));
    var imgDist = Math.min(image.width, image.height) / 2;
    var minScale = dist / imgDist;
    var dx = Math.cos(angle) * minScale;
    var dy = Math.sin(angle) * minScale; 
    ctx.setTransform(dx, dy, -dy, dx, ctx.canvas.width / 2, ctx.canvas.height / 2);
    ctx.drawImage(image, -image.width / 2, - image.height / 2);
    ctx.setTransform(1, 0, 0, 1, 0, 0);
}

更新 由于这是一个有趣的问题,我想看看我是否可以找出如何最好地适应旋转后的图像,以确保图像始终填满 Canvas ,同时还能显示尽可能多的图像。解决方案意味着当图像旋转时,比例会发生变化,并且由于边到了 Angular 落,比例有 4 个点,它突然停止收缩并再次开始增长。然而,这对于旋转但需要填充 Canvas 的静态图像很有用。

最好的描述方式是用图片 enter image description here 图像是绿色的, Canvas 是红色的。图像的旋转是 Angular R 我们需要找到直线 C-E 和 C-F 的长度。 Ih 和 Iw 是图像高度和宽度的一半。

我们从 Canvas 计算出 C-B 线的长度,它是 Canvas 高度 (ch) 和宽度 (cw) 的一半平方和的平方根。

然后我们需要 Angular A,它是 (ch) 的 acos/长度线 C-B

现在我们有了 Angular A,我们可以算出 Angular A1 和 A2。现在我们有了长度 C-B 和 Angular A1、A2,我们可以解决直 Angular 三 Angular 形 C-E-B 和 C-F-B 的 C-E 和 C-F。 这是 C-E = 长度 C-B * cos(A1) 和 C-F = 长度 C-B * cos(A2)

然后得到图像高度的比例,即长度 C-E 除以 Ih 和 C-E 除以 Iw。由于图像方面可能与我们需要适应的矩形不匹配,我们需要保持两个比例的最大值。

所以我们最终得到了一个合适的比例。

还有一个问题。数学运算适用于 0 -90 度的 Angular ,但对于其余的 Angular 则失败。幸运的是,每个象限的问题都是对称的。对于 > 180 度的 Angular ,我们镜像并向后旋转 180 度,然后对于 > 90 度 < 180 度的 Angular ,我们反转并向后旋转 90 度。我在演示中以弧度完成所有操作。我们还确保给定的 Angular 始终在 0 - 360(0 到 Math.PI * 2)范围内。

然后只需创建矩阵和绘制图像即可。

在演示中我添加了两个盒子。 red BLUE 是 Canvas 轮廓的一半尺寸副本,Blue RED 是图像的一半尺寸。我这样做是为了让您了解为什么图像在旋转时缩放不平滑。

我会为自己保留这个功能,因为它很方便。

获取新功能的Demo代码。

demo = function(){
    /** fullScreenCanvas.js begin **/
    var canvas = (function(){
        var canvas = document.getElementById("canv");
        if(canvas !== null){
            document.body.removeChild(canvas);
        }
        // creates a blank image with 2d context
        canvas = document.createElement("canvas"); 
        canvas.id = "canv";    
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight; 
        canvas.style.position = "absolute";
        canvas.style.top = "0px";
        canvas.style.left = "0px";
        canvas.style.zIndex = 1000;
        canvas.ctx = canvas.getContext("2d"); 
        document.body.appendChild(canvas);
        return canvas;
    })();
    var ctx = canvas.ctx;
    var image = new Image();
    image.src = "http://i.imgur.com/gwlPu.jpg";

    var w = canvas.width;
    var h = canvas.height;
    var cw = w / 2;  // half canvas width and height
    var ch = h / 2;
    // Refer to diagram in answer 
    function drawBestFit(ctx, angle, image){
        var iw = image.width / 2;  // half image width and height
        var ih = image.height / 2;
        // get the length C-B
        var dist = Math.sqrt(Math.pow(cw,2) + Math.pow(ch,2));
        // get the angle A
        var diagAngle = Math.asin(ch/dist);

        // Do the symmetry on the angle
        a1 = ((angle % (Math.PI *2))+ Math.PI*4) % (Math.PI * 2);
        if(a1 > Math.PI){
            a1 -= Math.PI;
        }
        if(a1 > Math.PI/2 && a1 <= Math.PI){
            a1 = (Math.PI/2) - (a1-(Math.PI/2));
        }
        // get angles A1, A2
        var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
        var ang2 = Math.abs(diagAngle - Math.abs(a1));
        // get lenghts C-E and C-F
        var dist1 = Math.cos(ang1) * dist;
        var dist2 = Math.cos(ang2) * dist;
        // get the max scale
        var scale = Math.max(dist2/(iw),dist1/(ih));
        // create the transform
        var dx = Math.cos(angle) * scale;
        var dy = Math.sin(angle) * scale; 
        ctx.setTransform(dx, dy, -dy, dx, cw, ch);
        ctx.drawImage(image, -iw, - ih);


        // draw outline of image half size
        ctx.strokeStyle = "red";
        ctx.lineWidth = 2 * (1/scale);
        ctx.strokeRect(-iw / 2, -ih / 2, iw, ih) 

        // reset the transform
        ctx.setTransform(1, 0, 0, 1, 0, 0);

        // draw outline of canvas half size
        ctx.strokeStyle = "blue";
        ctx.lineWidth = 2;
        ctx.strokeRect(cw - cw / 2, ch - ch / 2, cw, ch) 

    }

    // Old function
    function drawToFitRotated(ctx, angle, image){
        var dist = Math.sqrt(Math.pow(cw,2) + Math.pow(ch,2));
        var imgDist = Math.min(image.width, image.height) / 2;
        var minScale = dist / imgDist;

        var dx = Math.cos(angle) * minScale;
        var dy = Math.sin(angle) * minScale; 
        ctx.setTransform(dx, dy, -dy, dx, cw, ch);
        ctx.drawImage(image, -image.width / 2, - image.height / 2);
        ctx.setTransform(1, 0, 0, 1, 0, 0);
    }      

    var angle = 0;
    function update(){  // animate the image
        if(image.complete){
            angle += 0.01;
            drawBestFit(ctx,angle,image);
        }
        
        // animate until resize then stop remove canvas and flag that it has stopped
        if(!STOP){
            requestAnimationFrame(update);
        }else{
           STOP = false;
           var canv = document.getElementById("canv");
           if(canv !== null){
              document.body.removeChild(canv);
           }
            
        }
        
    }
    update();
}
/** FrameUpdate.js end **/
var STOP = false;  // flag to tell demo app to stop 
// resize Tell demo to stop then wait for it to stop and start it again
function resizeEvent(){
    var waitForStopped = function(){
        if(!STOP){  // wait for stop to return to false
            demo();
            return;
        }
        setTimeout(waitForStopped,200);
    }
    STOP = true;
    setTimeout(waitForStopped,100);
}
// if resize stop demo and restart
window.addEventListener("resize",resizeEvent);
 // start demo
demo();

关于javascript - 旋转后,在正确位置绘制图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34688514/

相关文章:

jquery - 背景居中时模糊 div 标签后面的内容

php - 简单的开/关餐厅开关

javascript - 如何使 Bootstrap 崩溃顺利

javascript - 进入页面时如何转到特定 anchor (不起作用)

javascript - 检查声音文件是否存在javascript

javascript - 变量中的横向 DOM

javascript - 从服务器获取 JSON 数据到表

Javascript:如果选择下拉选项,则显示文本框

php - 选中 Blob Image 并将其用作背景

css - 空间太大( float div)