在画布上绘制CSS转换(缩放、旋转、向左、向右)

Draw CSS transformation (Scale, Rotate, Left, Right) on Canvas

本文关键字:缩放 旋转 向左 向右 转换 绘制 CSS      更新时间:2023-09-26

我正在从用户的网络相机上拍摄照片,并将其绘制在画布上。我使用CSS构建了视频控件,如缩放视频、旋转视频、向左/向右移动。它适用于直播,但当我在画布上拍照和画画时,这些功能(旋转、缩放)不适用。我知道,因为我没有修改Canvas,所以它不适用。

所以,我想知道如何使用相同的CSS代码在画布上绘制[旋转、缩放、向左/向右移动]。(或者可能是Canvas上下文特定的代码)。

转换变得容易

不幸的是,给出的答案未能描述缩放、平移和旋转函数的正确用法。这些函数将现有的变换相乘,因此结果是相对的而不是绝对的。例如,从默认转换

ctx.setTransform(1, 0, 0, 1, 0, 0);  // set the transformation to the default identity matrix
ctx.scale(2, 2);  // scale the transform. Objects are now drawn 2 time larger
ctx.scale(2, 2);  // This is applied to the existing scale
                  // objects are now draw 4 times as large not 2 times
ctx.rotate(Math.PI / 2); // rotate the transformation 90 deg clockwise
                         // objects are drawn with the x axis down the screen
ctx.rotate(Math.PI / 2); // Rotate a further 90 deg clockwise
                         // objects are drawn with the x axis from right to left
                         // and the y axis moves up

ctx.translate(x, y)函数也是相对的,由于您提供的坐标是由现有变换变换的,因此计算起来更复杂。因此,如果在给出100乘100的平移的上述代码之后应用,将首先缩放并旋转4度和180度。得到的位置将位于画布坐标x:-400和y:-400处。要转换到所需的坐标(100100),需要首先应用逆变换,这将导致ctx.translate(-25, -25)

由于无法确定当前变换,因此很难计算逆变换并应用逆变换,以便在画布坐标中工作。

setTransform

画布2D API提供了一个函数ctx.setTransform(),该函数用新的转换替换当前转换。它不是相对的,而是绝对的。这使您能够确定当前的转换,并大大简化了转换图像(或任何正在绘制的图像)的过程

通用函数

在这里旋转缩放和定位图像是一个通用功能。

争论

  • ctx:要绘制到的2D画布上下文
  • image:要绘制的图像
  • x,y:放置中心的绝对画布坐标坐标,从画布左上角开始以像素为单位测量
  • centerX,centerY:图像原点相对于坐标图像的坐标(以像素为单位)。如果图像为100乘100,则将centerX、centerY设置为50,50将围绕图像中心缩放和旋转,并在自变量x和y给出的坐标处绘制该中心。如果centerX和centerY给定为0,0,则图像将围绕左上角旋转和缩放
  • 比例:绘制图像的比例。值1表示无刻度,2表示两倍大的0.5是其大小的一半。负数反转图像在x和y方向
  • 旋转:以弧度表示的旋转量,0表示否旋转然后以90度顺时针步进是CCD_ 4,Math.PIMath.PI * 1.5,返回起始Math.PI * 2

它的作用

该函数设置具有所需比例和平移的绝对变换。将旋转应用于该变换,然后绘制图像偏移,以将中心X、中心Y放置在所需坐标处。最后,函数将转换设置回默认值。如果您对所有转换都使用函数或setTransform,那么这是绝对不需要的,但我添加了它,以避免混淆80%依赖于当前默认转换的现有代码。

函数源代码

function drawImage(ctx, image, x, y, centerX, centerY, scale, rotate){
    ctx.setTransform(scale, 0, 0, scale, x, y);  // resets transform and 
                                                 // set scale and position
    ctx.rotate(rotate);  // apply the rotation to the above transformation
    ctx.drawImage(image, -centerX, -centerY);  // draw the image offset to its center
    ctx.setTransform(1, 0, 0, 1, 0, 0); // restore the transformation to default.
}

或者更简单的版本,它不会对默认转换进行不必要的重置

function drawImage(ctx, image, x, y, centerX, centerY, scale, rotate){
    ctx.setTransform(scale, 0, 0, scale, x, y);  // resets transform and 
                                                 // set scale and position
    ctx.rotate(rotate);  // apply the rotation to the above transformation
    ctx.drawImage(image, -centerX, -centerY);  // draw the image offset to its center
}

或者假设您总是使用图像中心

function drawImageCentered(ctx, image, x, y, scale, rotate){
    ctx.setTransform(scale, 0, 0, scale, x, y);  // resets transform and 
                                                 // set scale and position
    ctx.rotate(rotate);  // apply the rotation to the above transformation
    ctx.drawImage(image, -image.width / 2, -image.height / 2);  // draw the image offset to its center
}

用法

// image; is a 200 by 200 pixel image
// ctx; is the canvas 2D context
// canvas; is the canvas element
// call the function
drawImage(
    ctx,          // the context
    image,        // the image to draw
    canvas.width / 2, canvas.height / 2,  //draw it at the center of the canvas
    image.width / 2, image.height / 2,    // at the image center
    2,            // scale to twice its size
    Math.PI / 4   // and rotated clockwise 45 deg
); 

您不能通过CSS来实现,但在画布上绘制时需要使用一些javascript。对于缩放,您需要半手动地处理它,使用canvas.getContext()中上下文中drawImage的参数。基本上,参数2-5是从中提取图像的区域的(按顺序)、x、y、宽度和高度,参数6-9与将其放入图像的方式相同。

例如,如果你的基本图像是2000x2000,并且你正在将其成像为1000x1000:的画布

下面将把整个图像绘制成较小的图像。

var ctx = canvas.getContext("2d");
ctx.drawImage(img,0,0,2000,2000,0,0,1000,1000);

虽然这将把基础图像的中间1000x1000绘制到画布中(即2倍变焦):

var ctx = canvas.getContext("2d");
ctx.drawImage(img,500,500,1000,1000,0,0,1000,1000);

在上面的文章中,它说,从(500500)到(15001500)提取区域,并将其"打印"到画布中。

旋转更为复杂,因为如果只rotate图像并从画布的原点(0,0)绘制,则图像最终会离开画布。您还需要在画布上执行translate

var ctx = canvas.getContext("2d");
ctx.rotate(90 * Math.PI/180);    //rotate 90 degrees
ctx.translate(0, -canvas.height); //translate down the height of the canvas

var ctx = canvas.getContext("2d");
ctx.rotate(180 * Math.PI/180);    //rotate 180 degrees
ctx.translate(-canvas.width,-canvas.height);  // translate down and over

var ctx = canvas.getContext("2d");
ctx.rotate(270 * Math.PI/180);    //rotate 270 degrees
ctx.translate(-canvas.width,0);  // translate down and over

请注意,旋转发生在左上角周围。如果你想支持自由旋转,这一切都会变得更加复杂。

编辑:正如Blindman67在评论中正确指出的那样,转换应用于基于先前应用的任何转换的上下文的转换状态。我更新了代码示例,以澄清这是应用于干净上下文时的转换效果,而之前没有应用任何转换。