本文将介绍Canvas 中的缩放、平移、旋转和倾斜等基本变形功能的实现,并且利用这些基本变形的组合操作实现了图片的扭曲以及介绍一下色彩的渲染
一、变形
默认情况下,一个画布的坐标空间会使用画布的左上角 (0,0) 作为原点,x 值向右增加,y 值向下增加。这个坐标空间中的单位通常会被转换为像素,然后,可通过转换坐标空间在绘图过程中实现移动、缩放或旋转等操作。这些操作是通过 translate()、scale() 和 rotate() 等方法来实现的,它们会对画布的变换矩阵产生影响。
1.1 放大与缩小
用scale函数来实现图形的放大和缩小:
scale(x, y); 第一个参数 x 表示在 x 轴进行缩放,即水平缩放
第二个参数 y 表示在 y 轴上进行缩放,即在竖直方向上进行缩放
用scale函数参数为负来实现图形的翻转:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="myCanvas" width="500" height="350" style="background-color: #cccccc;">
你的浏览器不支持HTML5
</canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var image = new Image();
image.src = "face.jpg";
image.onload = function(){
ctx.drawImage(image,10,10);
ctx.scale(1,-1);
ctx.drawImage(image,250,-250);
};
</script>
</body>
</html>1.2 平移
使用 translate 函数可以实现对图形进行平移的功能。
translate (x, y);第一个参数 x 表示在 x 轴进行平移,即在水平方向上平移
第二个参数 y 表示在 y 轴上进行缩放,即在竖直方向上平移。
1.3 旋转
使用rotate函数可以实现图形的旋转功能。
rotate (angle);注意:传入rotate的参数angle是弧度不是角度。如果角度是angle,那么换算成弧度就是
angle*Math.PI/180。
以Canvas的起始坐标(0,0)为中心进行旋转:
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(200,50,100,50);
ctx.rotate(45*Math.PI/180);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(200,50,100,50);
</script>使用1.2中的translate函数,让图形以自己为中心旋转:
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(200,50,100,50);
/*先将Canvas的起始坐标移动到所画矩形的中心处,然后旋转,再将Canvas起始坐标返回*/
ctx.translate(250,75);
ctx.rotate(45*Math.PI/180);
ctx.translate(-250, -75);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(200,50,100,50);
</script>1.4 利用 transform 矩阵实现多样化的变形
上面分别讲了缩放、平移以及旋转的实现方法。其实所有这些变形都是可以通过变形矩阵 transform 来实现的。
transform (a,b,c,d,e,f); 该函数的各个变量对应以下变换矩阵中相应位置的参数:

1.4.1 缩放
设原始坐标为(x,y),缩放后坐标为(x1,y1)缩放倍数分别为a,d,那么:
x1 = a*x
x2 = d*y
得到矩阵公式:
这样就可以用transform(a,0,0,d,0,0)来代替scale(a,d)了。
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(10,10,150,100);
/*将原先的矩形沿x、y方向放大3倍,并重画一个灰色矩形*/
ctx.transform(3,0,0,3,0,0);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(10,10,150,100);
</script>1.4.2 平移
设原始坐标为(x,y),缩放后坐标为(x1,y1)x,y平移量分别为e,f,那么:
x1 = x+e
x2 = y+f
得到矩阵公式:
这样就可以用transform(1,0,0,1,e,f)来代替translate(e,f)了。
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(10,10,150,100);
ctx.transform (1,0,0,1,50,100);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(10,10,150,100);
</script>1.4.3 旋转

现在可以用 transform (cos θ, sin θ, –sin θ, cos θ,0,0) 来替换 rotate (θ) 了
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(200,50,100,50);
ctx.transform (Math.cos(45*Math.PI/180),Math.sin(45*Math.PI/180),-Math.sin(45*Math.PI/180), Math.cos(45*Math.PI/180),0,0);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(200,50,100,50);
</script>变换矩阵也可以通过 setTransform 函数来实现,setTransform 的参数与 transform 一样,不同的是,setTransform 函数是先消去之前的 transform 变换,然后重新进行变换的
transform:第二次在第一次的基础上再旋转10度,相当于旋转了15度
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(200,50,100,50);
/*首先旋转5度*/
ctx.transform (Math.cos(5*Math.PI/180),Math.sin(5*Math.PI/180),-Math.sin(5*Math.PI/180), Math.cos(5*Math.PI/180),0,0);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(200,50,100,50);
/*第二次在第一次的基础上再旋转10度,相当于旋转了15度*/
ctx.transform (Math.cos(10*Math.PI/180),Math.sin(10*Math.PI/180),-Math.sin(10*Math.PI/180), Math.cos(10*Math.PI/180),0,0);
ctx.beginPath();
ctx.strokeStyle = "#999999";
ctx.strokeRect(200,50,100,50);setTransform:清楚transform历史,重新旋转
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.strokeRect(200,50,100,50);
/*首先旋转5度*/
ctx.transform (Math.cos(5*Math.PI/180),Math.sin(5*Math.PI/180),-Math.sin(5*Math.PI/180), Math.cos(5*Math.PI/180),0,0);
ctx.beginPath();
ctx.strokeStyle = "#cccccc";
ctx.strokeRect(200,50,100,50);
/*清除第一次旋转5度的历史,重新对原来的图形旋转10度*/
ctx.setTransform (Math.cos(10*Math.PI/180),Math.sin(10*Math.PI/180),-Math.sin(10*Math.PI/180), Math.cos(10*Math.PI/180),0,0);
ctx.beginPath();
ctx.strokeStyle = "#999999";
ctx.strokeRect(200,50,100,50);
</script>1.4.4 倾斜
倾斜1:

图中3点p0、p2、p3遵循公式:
将上面的矩形公式代入steTransform函数中
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
ctx.setTransform(1,10/150,-40/100,1,40,10);
ctx.rect(50,50,150,100);
ctx.stroke();
</script>结果:
倾斜2:
图中3点p1、p2、p3遵循公式:
将上面的矩形公式代入steTransform函数中
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
ctx.setTransform(130/150,-20/150,-20/100,80/100,0,0);
ctx.rect(50,50,150,100);
ctx.stroke();
</script>结果:
1.4.5 图片扭曲效果
图中的扭曲变形效果需要通过多种变形组合来实现:
先实现两个倾斜的变形,然后利用clip函数江图片的一部分绘制出来:
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
var img = new Image();
img.src="face.jpg";
img.onload = function(){
ctx.save();
/*左半边图的绘制:以三个点为顶点绘制一个三角形,然后利用clip函数将这个三角形作为绘图的可视区域*/
/*以三个点为顶点绘制一个三角形*/
ctx.beginPath();
ctx.moveTo(80,0);
ctx.lineTo(320,40);
ctx.lineTo(0,200);
ctx.closePath();
ctx.clip();
/*以刚才三角形的三个顶点来进行倾斜变形*/
ctx.setTransform((320-80)/240,40/240,-80/240,200/240,80,0);
/*绘制图片:因为绘图的区域是一个三角形,所以绘制完的图片只有一部分*/
ctx.drawImage(img,0,0);
ctx.restore();
ctx.save();
/*右半边图的绘制,方法一样*/
ctx.beginPath();
ctx.moveTo(320,40);
ctx.lineTo(0,200);
ctx.lineTo(200,150);
ctx.closePath();
ctx.clip();
ctx.setTransform(200/240,(150-200)/240,(200-320)/240,(150-40)/240,0,200);
ctx.drawImage(img,0,0-240);
ctx.restore();
};
</script>二、图形的渲染
2.1 绘制颜色渐变效果的图形
颜色的渐变分为线性渐变和径向渐变
2.1.1 线性渐变
使用 createLinearGradient 函数和 addColorStop 函数可以实现线性渐变功能:
createLinearGradient(x1,y1,x2,y2)
其中的 4 个参数分别是渐变的出发点坐标 (x1,y1) 与终点坐标 (x2,y2)。
addColorStop (position,color)其中,position 参数必须是一个 0.0 到 1.0 之间的数值,表示渐变中颜色地点的相对地位;color 参数表示渐变的颜色。
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var grd=ctx.createLinearGradient(0,0,200,0);
grd.addColorStop(0.2,"#00ff00");
grd.addColorStop(0.8,"#ff0000");
ctx.fillStyle=grd;
ctx.fillRect(0,0,200,100);
</script>
2.1.1 径向渐变
使用 createRadialGradient 函数和 addColorStop 函数可以实现径向渐变功能。
createRadialGradient (x0,y0,r0,x1,y1,r1) 其中,参数 x0、y0 为开始圆的圆心坐标,r0 为开始圆的直径 ;x1、y1 为结束圆的圆心坐标,r1 为结束圆的直径。
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var grd=ctx.createRadialGradient(100,100,10,100,100,50);
grd.addColorStop(0,"#00ff00");
grd.addColorStop(1,"#ff0000");
ctx.fillStyle=grd;
ctx.fillRect(0,0,200,200);
</script>
2.2 颜色合成
globalCompositeOperation 属性说明了绘制到画布上的颜色是如何与画布上已有的颜色组合起来的。
使用代码:
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="#00ff00";
ctx.fillRect(10,10,50,50);
ctx.globalCompositeOperation="source-over";
ctx.beginPath();
ctx.fillStyle="#ff0000";
ctx.arc(50,50,30,0,2*Math.PI);
ctx.fill();
</script>下面列出了其中可能要设置的值以及它们的含义。
注:
- source 一词指的是将要绘制到画布上的颜色
- destination 指的是画布上已经存在的颜色
- 默认值是source-over
- copy :只绘制新图形,删除其他所有内容。
- darker :在图形重叠的地方,其颜色由两个颜色值相减后决定。
- destination-atop :画布上已有的内容只会在它和新图形重叠的地方保留。新图形绘制于内容之后。
- destination-in :在新图形及画布上已有图形重叠的地方,画布上已有内容都保留。所
- 有其他内容均为透明的。
- destination-out :在画布上已有内容和新图形不重叠的地方,已有内容保留。所有其他
- 内容均为透明的。
- destination-over :新图形绘制于画布上已有内容的后面。
- lighter :在图形重叠的地方,其颜色由两种颜色值的加值来决定。
- source-atop :只有在新图形和画布上已有内容重叠的地方才绘制新图形。
- source-in :在新图形以及画布上已有内容重叠的地方才绘制新图形。所有其他内容均为透明的。
- ource-out :只有在和画布上已有图形不重叠的地方才绘制新图形。
- source-over :新图形绘制于画布上已有图形的顶部。这是默认的设置。
- xor :在重叠和正常绘制的其他地方,图形都成为透明的。


2.3 颜色反转
所谓颜色反转,就是对图形的每个像素进行颜色取反
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var image = new Image();
image.src = "face.jpg";
image.onload = function(){
ctx.drawImage(image,0,0);
var imgdata = ctx.getImageData(0,0,250,250);
var pixels = imgdata.data;
// 遍历每个像素并对 RGB 值进行取反
for (var i=0, n=pixels.length; i<n; i+= 4){
pixels[i] = 255-pixels[i];
pixels[i+1] = 255-pixels[i+1];
pixels[i+2] = 255-pixels[i+2];
}
// 在指定位置进行像素重绘
ctx.putImageData(imgdata, 250, 0);
};
</script>
2.4 灰度控制
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var image = new Image();
image.src = "face.jpg";
image.onload = function(){
ctx.drawImage(image,0,0);
var imgdata = ctx.getImageData(0,0,250,250);
var pixels = imgdata.data;
// 遍历每个像素并对 RGB 值进行取反
for (var i=0, n=pixels.length; i<n; i+= 4){
var grayscale = pixels[i]*.3+pixels[i+1]*.59+pixels[i+2]*.11;
pixels[i ] = grayscale; // red
pixels[i+1] = grayscale; // green
pixels[i+2] = grayscale; // blue
}
// 在指定位置进行像素重绘
ctx.putImageData(imgdata, 250, 0);
};
</script>
2.5 阴影效果
Canvas API 中包含了自动为你所绘制的任何图形添加下拉阴影的属性。
阴影的颜色可用shadowColor 属性来指定,并且可以通过 shadowOffsetX 和 hadowOffsetY 属性来改变。
另外,应用到阴影边缘的羽化量也可以使用 shadowBlur 属性来设置。
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
/*设定阴影颜色为红色*/
ctx.shadowColor="#ff0000";
/*设定羽化程度为10*/
ctx.shadowBlur=10;
/*指定阴影的水平偏移量和垂直偏移量*/
ctx.shadowOffsetX=20;
ctx.shadowOffsetY=30;
var image = new Image();
image.src = "face.jpg";
image.onload = function(){
ctx.drawImage(image,0,0);
};
</script>本文内容是我对张路斌老师《HTML5 Canvas 游戏开发实战》第三章的学习总结,感谢张路斌老师,也感谢阅读本文的你给我的鼓励!
本章内容将会在下一篇进行一个实战,制作一个简单的自定义画板。
作为一个前端小白,如果学习笔记中有错误的地方,还请不吝指点,谢谢!