HTML5的Canvas中提供了路径旋转、移动、扩大/缩小等变形功能。
1、变形方法
在制作动画或绘制复杂的图形时,经常会用到两个变形方法transform()以及rotate()。在实际绘制前执行了这些方法后,实际绘制出来的图形中将显示旋转或变形。
setTransform(m11,m12,m21,m22,dx,dy) | 变形矩阵的指定(清空先前的指定) |
transform(m11,m12,m21,m22,dx,dy) | 变形矩阵的指定(可重复指定) |
rotate(angle) | 旋转 |
scale(x,y) | 扩大/缩小 |
translate(x,y) | 移动/变形 |
setTransform()与transform()的区别是,是否对已经设置的变形矩阵进行清空。transform()方法可重复制定不同效果的变形矩阵,而setTransform()方法被调用后将清空原先设置的变形矩阵。另外,提供旋转或扩大/缩小功能的rotate()方法、scale()方法与translate()方法都支持重叠变形矩阵。
save() | 记录变形矩阵的状态 |
restore() | 恢复变形矩阵的状态 |
2、移动与扩大/缩小
context.scale(x,y);
参数x为长度缩放的比例,参数y为宽带缩放的比例
context.translate(x,y);
(x,y)为基点移动后的坐标
简单案例:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>移动translate(x,y)放大/缩小scale(x,y)</title>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
<script type="text/javascript">
//取得canvas对象
var canvas=document.getElementById("canvas");
//获得描绘的上下文
var context=canvas.getContext("2d");
context.fillStyle="#a0a0a0";
context.fillRect(0,0,400,300);
//定义绘制矩形的方法
function drawRect(ctx,color){
ctx.fillStyle=color;
ctx.fillRect(20,20,50,50);
}
//绘制普通矩形
drawRect(context,"#cccccc");
//移动矩形以及扩大/缩小矩形
//第一种
//移动后的左上角坐标(x,y)计算
//x=2*(20+20),y=1*(20+0),width=2*50,height=1*50
/*
context.scale(2,1);
context.translate(20,0);
drawRect(context,"#c0000c");
*/
//第二种
//移动后的左上角坐标(x,y)计算
//x=40+2*20,y=0+1*20,width=2*50,height=1*50
context.translate(40,0);
context.scale(2,1);
drawRect(context,"#c0000c");
</script>
</body>
</html>
3、变形的保存与恢复
context.save();//变形的保存
context.restore();//变形的恢复
上述两种方法都是基于同一堆栈(stack)构造,调用save()方法后,将变形结果保存在堆栈顶端,调用restore()方法后,从堆栈顶端取出上次保存的结果用于恢复。
4、旋转
context.rotate(angle);
简单案例:通过移动/旋转等方式,分别绘制三个长方形。结合save()方法、restore()方法,以及rotate()方法。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>旋转</title>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
<script type="text/javascript">
//取得canvas对象
var canvas=document.getElementById("canvas");
//获取上下文
var ctx=canvas.getContext("2d");
//定义描绘矩阵的函数
function drawRect(context,color){
context.fillStyle=color;
context.fillRect(0,0,100,30);
}
//定义旋转函数
function rotateDeg(context,deg){
var rad=deg*Math.PI/180;//把“度”换成弧度
context.rotate(rad);
}
//绘制普通的矩形
drawRect(ctx,"red");
//指定移动、旋转后绘图
ctx.save();//保存状态
ctx.translate(100,30);//移动基点
rotateDeg(ctx,45);//旋转45度
drawRect(ctx,"green");
ctx.restore();//恢复状态
//指定移动、旋转后绘图
ctx.save();//保存状态
ctx.translate(200,50);//移动基点
rotateDeg(ctx,90);//旋转90度
drawRect(ctx,"blue");
ctx.restore();//恢复状态
</script>
</body>
</html>
上述程序中,首先绘制最初的红色长方形,接着,保存矩阵状态后,移动基点并旋转45度绘制新的绿色长方形。绿色长方形绘制结束后,恢复原先的矩阵状态,为绘制下一个长方形做准备。先移动基点,随后旋转90度,绘制蓝色的长方形。
注释掉第一个ctx.save();和ctx.restore();语句试试会发生哪些不同变化。
5、变形矩阵
context.setTransform(m11,m12,m21,m22,dx,dy);
其中的参数可变换为如下的矩阵
m11 m12 dx
m21 m22 dy
0 0 1
(1)、第1个参数m11
参数m11的默认值为1.0.当此值越大时,以Canvas区域的左侧面为基准,向右延伸坐标空间。相反当值比1.0小时,向左侧收缩坐标空间。m11为0时,实际坐标空间完全消失。但是,当m11的值为比0小的负数时,坐标空间变成以左侧为基准,向左侧延伸。
另外,在Firefox3.6中,当m11或m22的值比0小时,本来的图像将全被隐藏起来,其后当值重新恢复到0以上的数值时,隐藏的图像也不能恢复。
m11分别为0,0.5,1.0,1.5,2.0时,图像:
(2)、第2个参数m12
参数m12的默认值为0.0,当此值变大时,以canvas区域左侧面为基准,坐标空间向右下方向倾斜。相反当此值比0.0小时,以Canvas区域左侧面为基准,坐标空间向右上方向倾斜。
m12分别为-1,-0.5,0,0.5,1时,图像:
(3)、第3个参数m21
参数m21的默认值为0.0,当此值变大时,以canvas区域的上侧面为基准,坐标空间向右下方向倾斜。相反当此值比0.0小时,以Canvas区域的上侧面为基准,坐标空间向左下方向倾斜。
m21分别为-1.0,-0.5,0,0.5,1.0时,图像:
(4)、第4个参数m22
参数m22的默认值为1.0,当此值越大时,以Canvas区域的上侧面为基准,向下延伸坐标空间。相反当值比1.0小时,向上侧收缩坐标空间。m22为0时,实际坐标空间完全消失。但是,当m22的值为比0小的负数时,坐标空间变成以下侧为基准,向上延伸。
当m22的值分别为0,0.5,1.0,1.5,2.0时,图像:
(5)、第5个参数dx
参数dx的默认值为0,当此值越大时,Canvas的坐标空间向右移动。相反当此值变成负数时,坐标空间将向左移动。
当dx的值分别为-200,-100,0.0,100,200时,图像:
(6)、第6个参数dy
参数dy的默认值为0,当此值越大时,Canvas的坐标空间向下移动。相反当此值变成负数时,坐标空间将向上移动。
当dy的值分别为-200,-100,0.0,100,200时,图像:
6、实例---旋转图片动画
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>图标旋转效果(IE8不支持)</title>
<script type="text/javascript">
(function(){
//不支持window.addEventListener的浏览器不执行代码
if(!window.addEventListener){return;}
//定义canvas对象
var canvas=null;
//定义上下文对象
var ctx=null;
//动画的频率
var fps=60;
//图像对象
var image=null;
//旋转角度
var deg=0;
//页面导入时的事件处理
window.addEventListener("load",function(){
//img元素
image=document.getElementById("logo");
//图像尺寸
var w=parseInt(image.width);
var h=parseInt(image.height);
//创建canvas对象
canvas=document.createElement("canvas");
canvas.width=w;
canvas.height=h;
//创建canvas的上下文
ctx=canvas.getContext("2d");
//不支持canvas的浏览器返回
if(!ctx){return;}
//将图像绘制如canvas中
ctx.drawImage(image,0,0);
//用canvas替换img元素
image.parentNode.replaceChild(canvas,image);
//canvas的动画开始
move();
},false);
//旋转动画
function move(){
//canvas的尺寸
var cw=parseInt(canvas.width);
var ch=parseInt(canvas.height);
//初始化变形矩阵
ctx.setTransform(1,0,0,1,0,0);
//清空canvas
ctx.clearRect(0,0,cw,ch);
//计算变形矩阵
var m11=Math.cos(deg*Math.PI/180);
var dx=(cw/2)-(cw*m11/2);
//变形矩阵设置
ctx.setTransform(m11,0,0,1,dx,0);
//将变形后的图像绘制入canvas中
ctx.drawImage(image,0,0);
//旋转角度递增
deg++;
deg=deg%360;
//使用timer定时绘制下一幅图
setTimeout(move,1000/fps);
}
})();
</script>
</head>
<body style="text-align: center;">
<img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1548325966114&di=cd2d314c7491624d817795fee035b33d&imgtype=0&src=http%3A%2F%2Fwww.51yuansu.com%2Fpic2%2Fcover%2F00%2F48%2F21%2F5815e05f8afe4_610.jpg" id="logo"/>
</body>
</html>
详细解释move()函数 :
ctx.setTransform(1,0,0,1,0,0);//初始化变形矩阵
ctx.clearRect(0,0,cw,ch);//清空canvas
首先将变形矩阵恢复成默认值。便于坐标运算。
//计算变形矩阵
var m11=Math.cos(deg*Math.PI/180);
var dx=(cw/2)-(cw*m11/2);
为了让Canvas坐标平面看起来像在立体的旋转,只需要变动m11与dx即可。
在只变动m11的情况下,将以Canvas 区域的左侧面为基准进行左右伸缩。因此,通过使用dx来进行水平调整,使将向左右伸缩后的图像中心始终在Canvas区域的中心位置。
//变形矩阵设置
ctx.setTransform(m11,0,0,1,dx,0);
//将变形后的图像绘制入canvas中
ctx.drawImage(image,0,0);
将计算出来的数值m11和dx,传入到setTransform()方法中,重新设置变形矩阵。然后调用drawImage()方法重新绘制图像即可。