Javascript HTML5画布在缩放和旋转后向后平移
我正试着用画布做一些事情。首先,我让一个用户上传一个图像,如果图像比我想要的大,我需要缩小它。那部分很好用。最近我们遇到了iPhone用户上传图像的问题。这些都有定位问题。我已经知道了如何提取方向,我的问题是当我在画布中操纵图像时会发生什么 这就是我需要做的:获取图像,平移(),缩放(),旋转(),平移()480 | |尺寸[1]>853){ //缩放图像。 水平变量=宽度>高度; 如果(水平){ 标度高度=480; scaleRatio=缩放高度/高度; scaledWidth=宽度*比例; }否则{ 标度宽度=640; scaleRatio=缩放宽度/宽度; 缩放高度=高度*缩放比例; } 画布['width']=缩放宽度; 画布['height']=缩放高度; ctx['drawImage'](图像,0,0,宽度,高度,0,0,缩放宽度,缩放高度); /*旋转图像*/ 方向=8;//手动方向->在站点上,我们使用loadImage获取方向 如果(方向!=1){ 开关(方向){ 案例8: 案例6: canvas.width=缩放高度; canvas.height=缩放宽度; 打破 } var halfScaledWidth=scaledWidth/2; var halfScaledheight=缩放高度/2;Javascript HTML5画布在缩放和旋转后向后平移,javascript,html,canvas,Javascript,Html,Canvas,我正试着用画布做一些事情。首先,我让一个用户上传一个图像,如果图像比我想要的大,我需要缩小它。那部分很好用。最近我们遇到了iPhone用户上传图像的问题。这些都有定位问题。我已经知道了如何提取方向,我的问题是当我在画布中操纵图像时会发生什么 这就是我需要做的:获取图像,平移(),缩放(),旋转(),平移()480 | |尺寸[1]>853){ //缩放图像。 水平变量=宽度>高度; 如果(水平){ 标度高度=480; scaleRatio=缩放高度/高度; scaledWidth=宽度*比例;
ctx.save();//试试这个。你的代码是正确的,但是当你尝试旋转和缩放图像时(如果你想得到它),你必须改变方向变量。变换 对于简单的答案,如果你对如何跳到底部,你会找到一个替代方法来解决你的问题。这都是评论。我已经做了一些猜测,你想要什么
如果你感兴趣的是我认为使用2D变换函数的简单方法,请阅读其余部分。
矩阵数学 通过canvas 2D API使用“平移”、“缩放”和“旋转”时,所做的是将现有矩阵与每个函数创建的矩阵相乘 基本上当你这样做的时候ctx.rotate(Math.PI); // rotate 180 deg
API将创建一个新的旋转矩阵,并将现有矩阵与其相乘
与普通数学乘法不同,矩阵乘法将根据乘法的顺序改变结果。在普通数学乘法中,A*B=B*A但对于矩阵mA*mB!=mB*mA
(注意不等于)
当您需要应用多个不同的转换时,问题就更大了
ctx.scale(2,2);
ctx.rotate(Math.PI/2);
ctx.translate(100,100);
不会给出与相同的结果
ctx.scale(2,2);
ctx.translate(100,100);
ctx.rotate(Math.PI/2);
应用转换的顺序取决于您试图实现的目标。使用API这种方式对于复杂的链接动画非常方便。不幸的是,如果您不了解矩阵数学,这也会导致无尽的挫折。它还迫使许多人使用save
和restore
函数进行还原默认转换,即在某些情况下,GPU性能可能非常昂贵
setTransform()
不过我们很幸运,因为2D API也有函数ctx.setTransform(a、b、c、d、e、f)
这是您真正需要的功能。此功能将现有转换替换为提供的转换。大多数文档对a、b、c、d、e、f
的含义相当模糊,但其中包含旋转、缩放和平移
该函数的一个方便用法是设置默认转换,而不是使用保存和恢复
我经常看到这种类型的东西。(下面引用的示例1)
一种更简单的方法是直接删除保存,通过将变换设置为单位矩阵手动恢复和重置变换
ctx.scale(2,2);
ctx.rotate(Math.PI/2);
ctx.translate(100,100);
// draw the image
ctx.drawImage(img1, -img1.width / 2, -img1.height / 2);
ctx.setTransform(1,0,0,1,0,0); // restore default transform
// transform for image 2
ctx.scale(1,1);
ctx.rotate(Math.PI);
ctx.translate(100,100);
// draw the image
ctx.drawImage(img2, -img2.width / 2, -img2.height / 2);
ctx.setTransform(1,0,0,1,0,0); // restore default transform
现在,我相信您仍然想知道这些数字传递给setTransform的是什么,它们是什么意思
记住它们的最简单方法是2个向量和1个坐标。这两个向量描述单个像素的方向和比例,坐标只是原点的x,y像素位置(0,0处的绘图将位于画布上的位置)
像素及其轴
想象一个像素,这是抽象变换后的像素,可以通过当前变换进行缩放和旋转。它有两个轴,X轴和Y轴。要描述每个轴,我们需要两个数字(一个向量),这些数字描述屏幕(未转换)像素顶部和左侧的方向和比例。因此,对于与屏幕像素匹配的普通像素,X轴从左到右穿过顶部,长度为一个像素。矢量为(1,0
)一个像素宽,没有向下的像素。对于沿屏幕向下的Y轴,矢量为(0,1
)没有像素,向下一个像素。原点是屏幕右上角的像素,位于坐标(0,0
)
因此,我们得到了标识矩阵,2D API(和许多其他API)的默认矩阵,X轴(1,0
),Y轴(0,1
)和原点(0,0
),它们与setTransform(1,0,0,1,0,0)的六个参数相匹配
现在假设我们想放大像素。我们所做的就是增加X轴和Y轴的大小setTransform(2,0,0,2,0,0)
与scale(2,2)
(从默认变换)相同。我们的像素顶部现在在顶部有两个像素长,在左侧有两个像素长。要缩小setTransform(0.5,0,0.5,0,0)
我们的像素现在是半个像素
这两个轴向量(a,b)和(c,d)可以指向任何方向,彼此完全独立,它们不必彼此成90度角,因此可以使像素倾斜,也不要求它们具有相同的长度,以便可以更改p
// transform for image 1
ctx.save(); // save state
ctx.scale(2,2);
ctx.rotate(Math.PI/2);
ctx.translate(100,100);
// draw the image
ctx.drawImage(img1, -img1.width / 2, -img1.height / 2);
ctx.restore(); // restore saved state
// transform for image 2
ctx.save(); // save state agian
ctx.scale(1,1);
ctx.rotate(Math.PI);
ctx.translate(100,100);
// draw the image
ctx.drawImage(img2, -img2.width / 2, -img2.height / 2);
ctx.restore(); // restore saved state
ctx.scale(2,2);
ctx.rotate(Math.PI/2);
ctx.translate(100,100);
// draw the image
ctx.drawImage(img1, -img1.width / 2, -img1.height / 2);
ctx.setTransform(1,0,0,1,0,0); // restore default transform
// transform for image 2
ctx.scale(1,1);
ctx.rotate(Math.PI);
ctx.translate(100,100);
// draw the image
ctx.drawImage(img2, -img2.width / 2, -img2.height / 2);
ctx.setTransform(1,0,0,1,0,0); // restore default transform
// input vector
var x = 1;
var y = 0;
// rotated vector
var rx90 = -y; // swap y to x and make it negative
var ry90 = x; // x to y as is
// rotate again same thing
var rx180 = -ry90;
var rx180 = rx90;
// Now for 270
var rx270 = -ry180; // swap y to x and make it negative
var rx270 = rx180;
var x = 1; // one pixel across
var y = 0; // none down
var ox = canvas.width / 2; // center of canvas
var oy = canvas.height / 2;
ctx.setTransform(x, y, -y, x, ox, oy);
var scale = 2; // scale of 2
var ang = Math.random() * 100; // any random angle
var x = Math.cos(ang); // get the x axis as a unit vector.
var y = Math.sin(ang);
// scale the axis
x *= scale;
y *= scale;
// ctx is the 2D context,
// originX, and originY is the origin, same as ctx.translate(originX,originY)
// rotation is the angle in radians same as ctx.rotate(rotation)
// scale is the scale of x and y axis same as ctx.scale(scale,scale)
function createTransform(ctx,originX,originY,rotation,scale){
var x, y;
x = Math.cos(rotation) * scale;
y = Math.sin(rotation) * scale;
ctx.setTransform(x, y, -y, x, originX, originY);
}
// dont need ctx.save(); // save state
// dont need ctx.scale(2,2);
// dont need ctx.rotate(Math.PI/2);
// dont need ctx.translate(100,100);
createMatrix(ctx, 100, 100, Math.PI/2, 2)
// draw the image normally
ctx.drawImage(img1, -img1.width / 2, -img1.height / 2);
// dont need ctx.restore(); // restore saved state
// transform for image 2
// dont need ctx.save(); // save state agian
// dont need ctx.scale(1,1);
// dont need ctx.rotate(Math.PI);
// dont need ctx.translate(100,100);
// we don't have to reset the default transform because
// ctx.setTransform completely replaces the current transform
createMatrix(ctx, 100, 100, Math.PI/2, 2)
// draw the image
ctx.drawImage(img2, -img2.width / 2, -img2.height / 2);
// dont need ctx.restore(); // restore saved state
// this is in set up code
const MAX_SIZE_WIDTH = 640;
const MAX_SIZE_HEIGHT = 480;
orientationData = [];
orientationData[6] = Math.PI/2; // xAxis pointing down
orientationData[8] = -Math.PI/2; // xAxis pointing up
orientationData[3] = Math.PI; //xAxis pointing to left
// in your code
var orient,w,h,iw,ih,scale,ax,ay; // w and h are canvas size
// assume image is the loaded image
iw = image.width; // get the image width and height so I dont have to type as much.
ih = image.height;
if(orientation != 1){
var orient = orientationData[orientation];
if(orient === undefined){
return; // bad data so return
}
// get scale and resize canvas to suit
// is the image on the side
if(orientation === 6 || orientation === 8){
// on side so swap width and height
// get the height and width scales for the image, dont scale
// if the image is smaller than the dimension
scale = Math.min(1,
MAX_SIZE_WIDTH / ih,
MAX_SIZE_HEIGHT / iw
);
w = canvas.width = scale * ih;
h = canvas.height = scale * iw;
}else{
// for normal orientation
scale = Math.min(1,
MAX_SIZE_WIDTH / iw,
MAX_SIZE_HEIGHT / ih
);
h = canvas.height = scale * ih;
w = canvas.width = scale * iw;
}
// Do you really need to clear the canvas if the image is filling it??
// ensure that the default transform is set
ctx.setTransform(1, 0, 0, 1, 0, 0);
// clear the canvas
ctx.clearRect(0, 0, w, h);
// now create the transformation matrix to
// position the image in the center of the screen
// first get the xAxis and scale it
ax = Math.cos(orient) * scale;
ay = Math.sin(orient) * scale;
// now set the transform, the origin is always the canvas center
// and the Y axis is 90 deg clockwise from the xAxis and same scale
ctx.setTransform(ax, ay, -ay, ax, w / 2, h / 2);
// now draw the image offset by half its width and height
// so that it is centered on the canvas
ctx.drawImage(image,-iw / 2, -ih / 2);
// restore the default transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
} // done.