一、什么是canvas?
HTML5 <canvas>
元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成。
<canvas>
标签只是图形容器,必须使用脚本来绘制图形。
二、创建一个canvas画布
一个画布在网页中是一个矩形框,通过 <canvas>
元素来绘制。可以在HTML页面中使用多个 <canvas>
元素。
标签通常需要指定一个id属性 (脚本中经常引用),width 和 height 属性定义的画布的大小。
注意: width 和 height 使用的是元素的属性来设置,在style属性中设置的宽高是元素样式的宽高,不是canvas画布的宽高。
注意: 默认情况下 <canvas>
元素没有边框和内容。可以使用 style 属性来添加边框。
<canvas id="canvas" width="900" height="600"></canvas>
三、使用 JavaScript 来绘制图像——canvas绘图API的使用
使用canvas绘图API绘制图像之前,首先需要获取 canvas元素,设置canvas元素的宽高属性,创建 context 对象。
<canvas id="canvas"></canvas>
<script>
//获取canvas元素
var canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 900;
canvas.height = 600;
//将canvas转化为2d模型
var context = canvas.getContext("2d");
</script>
接下来使用canvas绘图API进行绘制:
下边举例说明常用的API属性和方法,更多内容参考:canvas API 中文网 或者 canvas API—MDN
1、填充和描边
context.fillStyle
填充样式。默认值是#000000纯黑色。
context.fill()
填充。
context.strokeStyle
描边样式。默认值是#000000纯黑色。
context.stroke()
描边。
2、绘制矩形
context.clearRect(x, y, width, height)
清除指定矩形区域内部所有的像素信息为初始色(通常为透明)。
context.fillRect(x, y, width, height)
矩形填充,可以填充颜色,渐变,图案等。
context.strokeRect(x, y, width, height)
矩形描边。
以上三个方法的参数相同,分别为:矩形的起始点横坐标、纵坐标、矩形的宽度、高度。
//清除一个矩形区域的内容
context.clearRect(0, 0, 900, 600);
//用红色填充一个矩形
context.fillStyle = "red";
context.fillRect(10, 10, 100, 100);
context.fill();
//用红色描边一个矩形
context.strokeStyle = "green";
context.strokeRect(230, 10, 100, 100);
context.stroke();
3、绘制文本
context.fillText(text, x, y [, maxWidth])
文字填充,可以填充纯色,渐变或者图案。
context.strokeText(text, x, y [, maxWidth])
文字描边。
以上两个方法的参数相同,分别为:
文本信息、
文本的起始点横坐标、
文本的起始点纵坐标、
文本占据的最大宽度(可选,当文本占据宽度超过此最大宽度的时候,通过压缩每个文本宽度进行适合,而非换行)。
//黄色填充文本
context.fillStyle = "yellow";
context.font = "bold 60px 幼圆";
context.fillText("Hello Word!", 350, 100, 300);
//蓝色描边文本
context.strokeStyle = "blue";
context.font = "bold 50px 等线";
context.strokeText("你好!", 700, 100, 200);
上例中的context.font
用来设置字体样式,其规则和 CSS 的 font 类似,参数分别为fontweight、fontsize、fontfamily。
除此之外还有其他设置字体样式的属性,如 textAlign、textBaseline、direction。
4、常用的线条样式设置
context.lineWidth
线条宽度,主使用场景是描边,默认宽度是1.0,支持小数。
context.lineCap
线条端点的样式。支持如下属性值:butt(默认值,断头,无端帽),round(圆形端帽),square(方形端帽)。
5、绘制路径
context.beginPath()
开始一个新路径。
context.closePath()
闭合一个路径。
context.moveTo(x,y)
路径绘制起始点。参数为落点的横坐标、纵坐标。
context.lineTo(x,y)
绘制直线到指定坐标点。参数为落点的横坐标、纵坐标。
context.arc(x, y, radius, startAngle, endAngle [, anticlockwise])
绘制圆弧(包括圆)。参数为圆心横坐标、圆心纵坐标、半径、圆弧开始的角度(单位是弧度)、圆弧结束的角度(单位是弧度)、弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算(true为逆时针,默认为false)。
context.arcTo(x1, y1, x2, y2, radius)
绘制圆弧,和之前的点以直线相连。参数为第一个控制点的横坐标、纵坐标、第二个控制点的横坐标、纵坐标、圆弧的半径。
context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
绘制椭圆路径。参数分别为:椭圆弧的圆心横坐标,纵坐标、长轴半径大小、短轴半径大小、椭圆弧的旋转角度(单位是弧度)、圆弧开始的角度(角度从横轴开始算,单位是弧度)、圆弧结束的角度(单位是弧度)、弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算(true为逆时针,默认为false)。
context.clip()
创建剪裁路径。使用的时候,先绘制裁剪路径,再执行clip() 方法,之后再绘制的内容就在这个裁剪路径中呈现。
context.strokeStyle = "green";
context.fillStyle = "yellow";
//绘制圆弧
context.beginPath();
context.arc(110, 200, 50, 0, Math.PI * 2, false);
context.stroke();
context.fill();
context.closePath();
//绘制线条
context.beginPath();
context.moveTo(200, 200);
context.arcTo(260, 150, 350, 260, 60);
context.arcTo(350, 260, 400, 200, 60);
context.lineTo(400, 200);
context.stroke();
context.closePath();
//绘制椭圆
context.beginPath();
context.ellipse(600, 200, 100, 50, Math.PI / 4, 0, Math.PI * 2, false);
context.stroke();
context.closePath();
下例中使用一个固定的路径裁剪图片:
//路径裁剪
var image1 = new Image();
image1.onload = function () {
context.clearRect(0, 0, 900, 600);
context.beginPath();
context.moveTo(200, 50);
context.lineTo(200, 100);
context.arcTo(100, 150, 200, 200, 50);
context.lineTo(200, 200);
context.lineTo(200, 250);
context.lineTo(250, 250);
context.arcTo(300, 400, 350, 250, 50);
context.lineTo(350, 250);
context.lineTo(400, 250);
context.lineTo(400, 200);
context.arcTo(500, 150, 400, 100, 50);
context.lineTo(400, 100);
context.lineTo(400, 50);
context.lineTo(350, 50);
context.arcTo(300, -50, 250, 50, 50);
context.lineTo(250, 50);
context.lineTo(200, 50);
context.clip();
context.closePath();
context.drawImage(this, 0, 0, 533, 300);
};
image1.src = "http://img5.imgtn.bdimg.com/it/u=3908114527,2766768530&fm=26&gp=0.jpg";
裁剪后的效果如下:
6、绘制图片
共有以下三种写法:
context.drawImage(image, dx, dy);
context.drawImage(image, dx, dy, dWidth, dHeight);
context.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
各个参数含义和作用如下:
image
绘制在Canvas上的元素,可以是各类Canvas图片资源,如<img>
图片,SVG图像,Canvas元素本身等。
dx
在Canvas画布上规划一片区域用来放置图片,dx就是这片区域的左上角横坐标。
dy
在Canvas画布上规划一片区域用来放置图片,dy就是这片区域的左上角纵坐标。
dWidth
在Canvas画布上规划一片区域用来放置图片,dWidth就是这片区域的宽度。
dHeight
在Canvas画布上规划一片区域用来放置图片,dHeight就是这片区域的高度。
sx
表示图片元素绘制在Canvas画布上起始横坐标。
sy
表示图片元素绘制在Canvas画布上起始纵坐标。
sWidth
表示图片元素从坐标点开始算,多大的宽度内容绘制Canvas画布上。
sHeight
表示图片元素从坐标点开始算,多大的高度内容绘制Canvas画布上。
其中sx、sy、sWidth、sHeight 可以看做是图片的裁剪。
//绘制图片
var image = new Image();
image.onload = function () {
context.drawImage(this, 0, 0, 533, 300, 50, 50, 533, 300);
};
image.src = "http://img5.imgtn.bdimg.com/it/u=3908114527,2766768530&fm=26&gp=0.jpg";
8、渐变相关
context.createLinearGradient(x0, y0, x1, y1);
创建线性渐变。参数为渐变起始点横坐标、纵坐标、渐变结束点横坐标、纵坐标。
context.createRadialGradient(x0, y0, r0, x1, y1, r1);
创建径向渐变。参数为:起始圆的横坐标、纵坐标、起始圆的半径、结束圆的横坐标、纵坐标、结束圆的半径。
线性渐变中,需要注意的是,参数中的坐标是相对于全局的,而不是相对于填充元素计算的;并且,如果渐变坐标在Canvas外部,也只会显示画布内的渐变效果。
以上两个方法一般和
addColorStop(offset, color)
方法连用。
addColorStop(offset, color)
方法用来给渐变添加新的渐变点,参数为:渐变点相对于整个渐变范围的偏移(范围是0-1),渐变点的颜色值(只要能被正确解析为CSS颜色的值都是合法的)。
//线性渐变
var gradient = context.createLinearGradient(0, 150, 900, 150);
gradient.addColorStop(0.2, "red");
gradient.addColorStop(0.4, "orange");
gradient.addColorStop(0.6, "yellow");
gradient.addColorStop(0.8, "green");
gradient.addColorStop(1, "cyan");
context.fillStyle = gradient;
context.fillRect(0, 0, 900, 200);
//径向渐变——圆形渐变
//起始圆半径为0
var grad = context.createRadialGradient(200, 400, 0, 200, 400, 100);
grad.addColorStop(.2, "white");
grad.addColorStop(.5, "yellow");
grad.addColorStop(1, "orange");
context.fillStyle = grad;
context.beginPath();
context.arc(200, 400, 100, 0, Math.PI * 2, false);
context.fill();
context.stroke();
context.closePath();
//渐变色字
var g = context.createLinearGradient(400, 500, 900, 500);
g.addColorStop(0.2, "red");
g.addColorStop(0.4, "orange");
g.addColorStop(0.6, "yellow");
g.addColorStop(0.8, "green");
g.addColorStop(1, "cyan");
context.fillStyle = g;
context.font = "700 60px 幼圆";
context.fillText("Hello World!", 400, 400, 500);
9、图案相关
context.createPattern(image, repetition)
创建图案。图案内容可以是图片,可以是<canvas>
元素,也可以是渐变。参数为:用来平铺的CanvasImageSource图像、平铺方式(repeat,repeat-x,repeat-y,no-repeat)
var image = new Image();
image.onload = function () {
var pattern = context.createPattern(this, "repeat");
context.fillStyle = pattern;
context.font = "700 60px 幼圆";
context.fillText("Hello World!", 400, 550, 500);
};
image.src = "http://img3.imgtn.bdimg.com/it/u=3681705955,4153554780&fm=26&gp=0.jpg";
10、阴影相关
context.shadowColor
指定阴影的颜色。默认值是透明黑,也就是看不到颜色,因此,如果我们想要使用阴影效果,shadowColor是必须要指定的。
context.shadowBlur
指定阴影的模糊程度。默认值是0,表示不模糊。
context.shadowOffsetX
阴影的水平偏移,默认值是0。
context.shadowOffsetY
阴影的垂直偏移,默认值是0。
//阴影
context.shadowColor = "#c0c0c0";
context.shadowBlur = 3;
context.shadowOffsetX = 8;
context.shadowOffsetY = 8;
context.lineWidth=10;
context.strokeStyle="blue";
context.strokeRect(300,300,200,200);
11、透明度
context.globalAlpha
设置画布的全局透明度,范围是0到1,0表示完全透明,1表示完全不透明,范围以外的值会被忽略。
12、变换
(1)旋转:
context.rotate(angle);
给canvas画布添加旋转矩阵,顺时针方向,单位是弧度。
默认旋转中心点是Canvas的左上角(0, 0)坐标点,如果希望改变旋转中心点,例如以Canvas画布的中心旋转,需要先使用translate()
位移旋转中心点。
注意: 此旋转和CSS3的旋转变换不一样,旋转的是坐标系,而非元素。因此,实际开发的时候,旋转完毕,需要设置 context.setTransform(1, 0, 0, 1, 0, 0);
将坐标系再还原。
比如:
// 文字旋转45度
context.rotate(45 * Math.PI / 180);
// 字体填充
context.font = '20px STHeiti, SimHei';
context.fillText('旋转,跳跃,我闭着眼', 60, -40, 188);
// 重置当前的变换矩阵为初始态
context.setTransform(1, 0, 0, 1, 0, 0);
(2)缩放
context.scale(x, y);
用来缩放Canvas画布的坐标系,只是影响坐标系,之后的绘制会受此方法影响,但之前已经绘制好的效果不会有任何变化。
默认缩放中心点是Canvas的左上角(0, 0)坐标点,如果希望改变缩放中心点,需要先使用translate()方法进行位移。
此缩放支持负数,也支持小数。如果参数 x,y 的值是-1,分别表示水平翻转和垂直翻转。
(3)位移
context.translate(x, y);
对canvas坐标系进行整体位移,实际开发中经常用来改变其它变换方法的中心点。 参数分别为坐标系水平和垂直位移的距离。
(4)当前矩阵变换基础上再次矩阵变换
context.transform(a, b, c, d, e, f);
对当前坐标系进行进一步变换,以实现缩放、旋转、拉伸、位移效果。
各个参数的含义分别为:水平缩放、水平斜切、垂直斜切、垂直缩放、水平位移、垂直位移。
设置 context.transform(1, 0, 0, 1, 0, 0);
即:维持当前的变换矩阵不变。
注意:
此方法和setTransform()
方法的区别在于,后者一旦执行会完全重置已有的变换,transform()
方法则是累加。
(5)直接重置为当前设置的矩阵变换
context.setTransform(a, b, c, d, e, f);
通过矩阵变换重置当前的坐标系。
各参数的含义和 transform()
方法相同。
设置 context.setTransform(1, 0, 0, 1, 0, 0);
即:重置当前的变换矩阵为初始态。
13、图片与像素
context.createImageData(width, height);
context.createImageData(imagedata);
创建一个全新的空的ImageData对象。该对象中的所有像素信息都是透明黑。返回值是ImageData对象,包含width,height和data这3个只读属性。
以上参数的含义:
width ——Number
ImageData对象包含的width值。如果ImageData对象转换成图像,则此width也是最终图像呈现的宽度。
height ——Number
ImageData对象包含的height值。如果ImageData对象转换成图像,则此height也是最终图像呈现的高度。
imagedata ——Object
一个存在的ImageData对象,只会使用该ImageData对象中的width和height值,包含的像素信息会全部转换为透明黑。
context.getImageData(sx, sy, sWidth, sHeight);
返回一个ImageData对象,其中包含Canvas画布部分或完整的像素点信息。
以上参数分别为:需要返回的图像数据区域的起始横坐标、需要返回的图像数据区域的起始点纵坐标、需要返回的图像数据区域的宽度、需要返回的图像数据区域的高度。
注意:当我们需要对图像进行像素级处理的时候,getImageData() 方法是不可或缺的。
getImageData()方法可能会出现CORS跨域报错,具体方法参见 解决canvas图片getImageData,toDataURL跨域问题
context.putImageData(imagedata, dx, dy);
将给定ImageData对象的数据绘制到位图上。
此方法不受画布变换矩阵的影响。
参数分别为:包含图像像素信息的 ImageData 对象、目标Canvas中被图像数据替换的起点横坐标、纵坐标。
应用示例:
绘制一张图片到Canvas画布上,然后把中间300*300区域变成灰色。
<canvas id="canvas"></canvas>
<script>
var canvas = document.querySelector("canvas");
//设置画布的尺寸
canvas.width = 1024;
canvas.height = 768;
var image = new Image();
image.onload = function () {
//先将canvas 转化为2d模型
var context = canvas.getContext("2d");
//图片绘制
context.drawImage(this, 0, 0, 1024, 768);
//获取中间300*300区域数据
var imageData=context.getImageData(362,234,300,300);
console.log(imageData);
var length=imageData.data.length;
for(var index=0;index<length;index+=4){
var r=imageData.data[index];
var g=imageData.data[index+1];
var b=imageData.data[index+2];
//计算灰度
var gray=r*0.299+g * 0.587 + b * 0.114;
imageData.data[index]=gray;
imageData.data[index+1]=gray;
imageData.data[index+2]=gray;
}
//更新新数据
context.putImageData(imageData,362,234);
};
image.src = "./image/1.jpg";
</script>
14、canvas状态
canvas.save()
保存当前Canvas画布状态并放在栈的最上面。可以使用restore()方法依次取出。
canvas.restore()
依次从堆栈的上方弹出存储的canvas状态,如果没有任何存储的canvas状态,则执行此方法没有任何变化。
举例:
先存储默认的Canvas状态,再还原,可以看到填充颜色变成了默认的黑色了。代码如下:
// 保存初始Canvas状态
context.save();
// 设置红色填充
context.fillStyle = 'red';
// 矩形填充
context.fillRect(20, 20, 100, 60); //红色
// 还原在绘制
context.restore();
// 矩形填充again
context.fillRect(180, 60, 100, 60); //黑色