<canvas> 标签用于绘制图像(通过脚本,通常是 JavaScript)。<canvas> 元素本身并没有绘制能力(它仅仅是图形的容器/画布),可以通过多种方法使用 canvas 绘制路径,盒、圆、字符以及添加图像。
一个画布在网页中是一个矩形框,通过 <canvas> 元素来绘制。需要赋予id,width,height属性。
<canvas id="myCanvas" width="200" height="100"></canvas>
可以通过style赋予背景颜色,边框等属性
画布建成以后,剩下的绘制工作都通过脚本去绘制。绘制分为两个步骤:初始化画布对象 与 对应的绘制操作。
初始化画布对象有两步:
①获取画布元素:如
var c=document.getElementById("myCanvas");
②通过getContext() 方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。如
var ctx=c.getContext("2d");
之后的绘制只需要对ctx对象操作即可。
备注:
画布是矩形框,以矩形框的左上角为原点构建二维坐标系,向右为x轴正方向,向下为y轴正方向,如图所示
绘制矩形:
strokeRect(x,y,width,height)方法绘制空心矩形(x,y为起点坐标,width,height为矩形宽度长度)
fillRect(x,y,width,height) 绘制实心矩形。
strokeStyle属性设置空心矩形线条颜色,lineWidth属性设置空心矩形的线条宽度,lineCap属性设置线条端点形状,lineJoin属性设置线条交点的形状。
fillStyle属性设置实心矩形颜色。
ctx.strokeStyle = "#0000ff";
ctx.lineWidth = 5;
ctx.strokeRect(10, 10, 80, 80)
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
绘制线条:
moveTo(x,y) 定义线条开始坐标
lineTo(x,y) 定义线条结束坐标
stroke()方法绘制路径。
ctx.moveTo(0,0);
ctx.lineTo(200,100);
ctx.stroke();
备注:
①连续的线条可以一次使用多个lineTo()方法。
②线条绘制出来的图形若为封闭图形,也可使用fill()绘制图形,且可以填充颜色(即可以用线条去画出三角形,五边形等自定义形状且填色)
ctx.moveTo(30, 30)
ctx.lineTo(40, 10)
ctx.lineTo(50, 30)
ctx.lineTo(70, 30)
ctx.lineTo(50, 50)
ctx.lineTo(30, 30)
ctx.fill()
绘制圆形:
arc(x,y,r,start,stop)定义圆形(x,y为圆心坐标,r为半径,start,stop为起始角度和结束角度,以弧度表示)
stroke()方法绘制空心圆
fill()方法绘制实心圆。
ctx.arc(95,50,40,0,2*Math.PI);
ctx.stroke();
ctx.arc(95, 50, 40, 0, 2 * Math.PI);
ctx.fillStyle = "#009900";
ctx.fill()
绘制文本:
font属性定义字体
strokeText(text,x,y)方法绘制空心的文本
fillText(text,x,y)方法绘制实心的文本
ctx.font="30px Arial";
ctx.strokeText("Hello World",10,50);
ctx.font="30px Arial";
ctx.fillText("Hello World",10,50);
放置图像:
drawImage(image,x,y,width,height)方法放置图像,其中image为图像对象(需要先获取),x与y为图像左上角坐标,width,height为图像的宽高。
var img = document.getElementById('myImg')
// 也可以使用js创建图像标签
// var img = new Image();
// img.src = 'http://www.runoob.com/images/pulpit.jpg';
ctx.drawImage(img, 10, 10, 180, 80)
补充:可以对源图片进行裁剪处理
drawImage(image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight)
第一个参数和其它的是相同的,都是一个图像或者另一个 canvas 的引用。
其他 8 个参数:
前 4 个是定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小。
更多:
1.如果要对ctx对象进行多次绘制 需要通过beginPath()方法开始一个新的路径。而如果要将图形变成一个封闭图形,可使用closePath()方法使用直线封闭图形,如
ctx.moveTo(0, 0)
ctx.lineTo(200, 100)
ctx.stroke()
ctx.beginPath()
ctx.arc(95, 50, 40, 0, Math.PI)
ctx.closePath()
ctx.stroke()
2.渐变色设置:线条渐变 与 径向/圆渐变
①设置渐变方案
createLinearGradient(x,y,x1,y1)方法创建线条渐变(箭头,x,y为起点坐标,x1,y1为终点坐标,起点左侧的全部区域是开始渐变的颜色,重点右侧的全部区域是结束渐变的颜色,箭头上下的区域按照渐变方向进行统一调整)
createRadialGradient(x,y,r,x1,y1,r1)方法创建一个径向/圆渐变区域(圆形,x,y为起始圆圆心坐标,r为起始圆半径,x1,y1为终止圆圆心坐标,r1为终止圆半径)
备注:渐变方案的坐标原点是画布的原点,与绘制的矩形无关
var grd = ctx.createLinearGradient(20, 0, 180, 0);
②添加渐变颜色(两个及以上)
addColorStop( 位置, 颜色 )方法(位置用数字表示,可以是0-1,0为起点,1为终点)
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "skyblue");
grd.addColorStop(1, "white");
③将渐变方案设置给需要的属性,如strokeStyle或fillStyle
ctx.fillStyle = grd;
④再进行绘制图形即可。
全部代码如下:
<canvas id="myCanvas" width="200" height="100" style="background-color: pink;"></canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var grd = ctx.createLinearGradient(0, 0, 180, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "skyblue");
grd.addColorStop(1, "white");
ctx.fillStyle = grd;
ctx.fillRect(10, 10, 180, 80);
</script>
3.样式的保存与恢复
save()方法可以保存当前画布对象的参数(可以调用多次,相当于入栈push):
-
变形(移动,旋转和缩放)
-
strokeStyle
,fillStyle
,globalAlpha
,lineWidth
,lineCap
,lineJoin
,miterLimit
,shadowOffsetX
,shadowOffsetY
,shadowBlur
,shadowColor
,globalCompositeOperation 的值
-
当前的遮罩层
restore()方法可以将保存的方法恢复(可以调用多次,相当于出栈pop)
ctx.fillRect(0, 0, 150, 150); // 使用默认设置绘制一个矩形
ctx.save(); // 保存默认状态
ctx.fillStyle = 'red' // 在原有配置基础上对颜色做改变
ctx.fillRect(15, 15, 120, 120); // 使用新的设置绘制一个矩形
ctx.save(); // 保存当前状态
ctx.fillStyle = '#FFF' // 再次改变颜色配置
ctx.fillRect(30, 30, 90, 90); // 使用新的配置绘制一个矩形
ctx.restore(); // 重新加载之前的颜色状态
ctx.fillRect(45, 45, 60, 60); // 使用上一次的配置绘制一个矩形
ctx.restore(); // 加载默认颜色配置
ctx.fillRect(60, 60, 30, 30); // 使用加载的配置绘制一个矩形
4.变形(移动,旋转,缩放)
①移动translate
translate(x,y)方法用来移动canvas的原点到指定的位置(x为向右移动的距离,y为向下移动的距离)
ctx.strokeRect(0, 0, 100, 50);
ctx.translate(20, 20);
ctx.strokeRect(0, 0, 100, 50);
②旋转rotate
rotate(angle)方法旋转坐标轴(angle为旋转的角度,弧度制,顺时针方向)
ctx.strokeRect(0, 0, 100, 50);
ctx.rotate(45 / 180 * Math.PI)
ctx.strokeRect(0, 0, 100, 50);
③缩放scale
scale(x,y)方法对画布进行缩放(x,y控制x轴,y轴的缩放,必须是正值,且比1小表示缩小,比1大表示放大,等于1则无效果)
ctx.strokeRect(0, 0, 100, 50);
ctx.scale(1.5, 1.5)
ctx.strokeRect(0, 0, 100, 50);
5.多图形之间的显示规则
globalCompositeOperation属性可以改变默认的显示规则(默认:新图形若与老图形重叠,则新图形会覆盖在老图形之上)设置globalCompositeOperation属性之前的图形为老图形,设置之后的图形为新图形,可以填写的值有:
值 | 描述 |
---|---|
destination-over | 新图像在老图像的下面 |
source-in | 新图像只显示与老图像重叠的部分,其他部分全部透明。(老图像透明) |
source-out | 新图像只显示与老图像没有重叠的部分,其余部分全部透明。(老图像透明) |
source-atop | 新图像只显示与老图像重叠的部分,其余部分全部透明。(老图像显示) |
destination-in | 老图像只显示与新图像重叠的部分,其他部分全部透明。(新图像透明) |
destination-out | 老图像只显示与新图像没有重叠的部分,其余部分全部透明。(新图像透明) |
destination-atop | 老图像只显示与新图像重叠的部分,老图形在上。 |
xor | 重叠部分会变成透明 |
copy | 只有新图像会被保留,其余的全部变透明。 |
lighter | 新老图像都显示,但是重叠部分的颜色做加处理。 |
darken | 保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的) |
lighten | 保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的) |
ctx.lineWidth = 10
ctx.moveTo(0, 0)
ctx.lineTo(100, 100)
ctx.stroke()
ctx.globalCompositeOperation = "xor";
ctx.beginPath()
ctx.strokeStyle = 'red'
ctx.moveTo(200, 0)
ctx.lineTo(0, 100)
ctx.stroke()
6.遮罩层(裁剪)
clip()方法实现遮罩层,只显示设置clip()之前的区域,区域外的部分会被隐藏。(类似于设置globalCompositeOperation属性的source-atop值,不同的是clip()只需要设置区域,不需要创建图形,而globalCompositeOperation属性需要一个老图形)
ctx.arc(0, 0, 100, 0, Math.PI * 2);
ctx.clip();
//只需要给出圆形范围,不需要fill()或stroke()方法去创建圆形
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 200, 100);
7.弧线
①圆弧:arc(x, y, r, startAngle, endAngle)方法实现,设置第四五个参数的范围即可
ctx.arc(50, 50, 40, 0, Math.PI / 2);
ctx.stroke();
②圆弧:arcTo(x1, y1, x2, y2, radius): : 根据给定的控制点和半径画一段圆弧,最后再以直线连接两个控制点(个人感觉不怎么实用?主要还是用贝塞尔曲线)。
ctx.moveTo(50, 50);
ctx.arcTo(200, 50, 200, 200,150);
ctx.stroke();
③二次贝塞尔曲线:quadraticCurveTo(cp1x,cp1y,x,y)方法创建二次贝塞尔曲线(cp1x,cp1y是控制点坐标,x,y是结束点坐标)
④三次贝塞尔曲线:bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)方法创建三次贝塞尔曲线(cp1x,cp1y是控制点1坐标,cp2x,cp2y是控制点2坐标,x,y是结束点坐标)
对贝塞尔曲线的个人理解:首先先确定起始点后终点坐标(起始点通过moveTo()方法给出,终点是贝塞尔方法的最后两个参数),然后通过一个或两个控制点去拉扯曲线,让曲线的轨迹符合你的需要,这时候就需要不断调整控制点的坐标了,以及控制点越多自然越精确。理论上讲所有曲线都可以通过贝塞尔曲线实现。
ctx.moveTo(40, 200);
ctx.bezierCurveTo(20, 100, 100, 120, 200, 200);
ctx.stroke();
9.动画
通过定时器实现,埋坑,后面再学。
备注:
1.通过画布对象c的toDataURL()方法,可以将画布中的内容导出,返回参数为图片地址
let c = document.getElementById('myCanvas')
let ctx = c.getContext('2d')
let imgUrl = c.toDataURL('image/png')
2.如果绘制的图形初始位置贴边的话,clearRect()貌似会出bug清除不干净
3.isPointInPath()方法可以判断某一个点是否在绘制区域内,返回布尔值:
isPointInPath(x,y)判断点是否在当前绘制的图形区域内(未被closePath关闭)
isPointInPath(路径对象,x,y)判断点是否在参数1的图形区域内(可以通过new一个Path2D对象存储路径)
let p = new Path2D()
p.arc(10, 10, 10, 0, 2 * Math.PI) //此时路径就保存在p对象里
ctx.fill(p) //绘制p对象即可
c.addEventListener('mousemove', (e) => {
if (ctx.isPointInPath(p, 10, 10)) {
console.log('在')// 坐标(10,10在p路径内,所以会执行这条语句)
}
})