前段时间在公司没什么事干,研究了一下canvas,在实际开发中还没正式应用过,但是已经深深感觉到其魅力之处。下面写一写我认为canvas中比较重要的点,如有理解错误,欢迎指正。
首先canvas是h5中的新增一个元素,先创建一个h5文件,然后像写一个div一样,在页面初始化一个canvas,由于之后canvas的操作都是由js完成,为了操作方便,赋予canvas一个id值,给width和height作为canvas的宽高。话不多说,上代码先
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <canvas id="drawing" width="200" height="200"> A draw of something</canvas> <script> var drawing = document.getElementById(‘drawing‘); //确定浏览器支持canvas元素 if(drawing.getContext) { var context = drawing.getContext("2d"); } </script> </body> </html>
上面就是初始化一个包含canvas元素的最基础写法啦,然后我们就要动手画了。
canvas画图我给分为了两种,一种是canvas自身创建,另外一种就是canvas中引用了img,先说canvas自身创建。canvas自身创建方式首先有两种,一种是描边即stroke,另外一种是填充fill,两种方式均可以定义使用的样式,包括描边或填充的色值、效果(如渐变)等;另外canvas自身创建可以使用canvas提供的现有的图形矩形和弧形也可以通过定义路径来形成图形。比较规则的直线或可以通过lineTo()方法,不规则的曲线可以通过贝塞尔曲线来完成。
下面讲一下绘制图形过程中,比较重要的几个概念。
1.beginPath()
beginPath()即开始或重置一条路径,我们在实际应用中,会经常用到。我们在单纯使用arc或rect相关方法,并不涉及到路径时可以不用beginPath(),但是当我们使用如lineTo(),贝塞尔曲线等这种使用路径的方法绘制图形时,则必须要使用beginPath();当然了,有beginPath(),就有closePath();如果我们使用的是stroke()方法,调用了beginPah(),就要相应地调用closePath()才可以把路径闭合,比如绘制下面一个三角形
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title>canvas小试牛刀</title> 6 <style> 7 canvas { border: 1px solid black; } 8 </style> 9 </head> 10 <body onload = "draw();"> 11 <canvas id="tutorial" width="450" height="250"></canvas> 12 <script> 13 function draw(){ 14 var canvas = document.getElementById(‘tutorial‘); 15 16 if(canvas.getContext) { 17 var ctx = canvas.getContext(‘2d‘); 18 19 // 绘制一个三角形 20 ctx.beginPath(); 21 ctx.moveTo(50, 50); 22 ctx.lineTo(100, 50); 23 ctx.lineTo(75, 100); 24 // ctx.fillStyle = "rgba(0,0,255,0.2)"; 25 ctx.closePath();//闭合路径 26 ctx.stroke();//默认描边的颜色是黑色 27 } 28 } 29 30 </script> 31 </body> 32 </html>
当前代码运行完毕之后的效果是这样的
如果我们去掉closePath(),效果是这样
因为我们在代码中实际只绘制了两条直线,closePath()帮我们把最后一个点连接到起点以达到闭合路径的作用,当然,也可以不调用closePath(),选择自行写代码闭合也是完全可以的。而当我们把closePath()和stroke()分别注掉,把fillStyle()放出来,再加上ctx.fill(),效果是这样的
所以在使用填充的时候closePath()就没什么卵用了,使用填充的时候,会自动闭合路径,另外友情提示一下,虽然是自动闭合,但是所谓自动闭合就是最后一个点与起点的连线,所以说,如果你的图形是曲线或是不规则的还是自己闭合吧
2关于moveTo与translate()
moveTo()是把坐标移动到某一点,其主要作用是避免出现不必要的线,嗯,来个例子吧,比较直观一点
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title>canvas小试牛刀</title> 6 <style> 7 canvas { border: 1px solid black; } 8 </style> 9 </head> 10 <body onload = "draw();"> 11 <canvas id="tutorial" width="450" height="250"></canvas> 12 <script> 13 function draw(){ 14 var canvas = document.getElementById(‘tutorial‘); 15 16 if(canvas.getContext) { 17 var ctx = canvas.getContext(‘2d‘); 18 19 // 绘制一个笑脸 20 ctx.beginPath(); 21 ctx.arc(75,75,50,0,2*Math.PI,false); 22 23 ctx.moveTo(110,75); 24 ctx.arc(75,75,35,0,Math.PI,false); 25 26 ctx.moveTo(65,65); 27 ctx.arc(60,65,5,0,2*Math.PI,true); 28 29 ctx.moveTo(95,65); 30 ctx.arc(90,65,5,0,2*Math.PI,true); 31 ctx.stroke(); 32 } 33 } 34 35 </script> 36 </body> 37 </html>
上面代码运行后的效果是这样
如果我去掉第一个moteTo()方法即
ctx.moveTo(110,75);
效果会是这样,
两个路径的终点和起点之间会连在一起,多出没用的线。就像我们在用笔画一个笑脸时,先画了一个圆作为头,此时笔尖先不要动,我们接下来要画嘴巴那个半圆,如果我们的笔尖这个时候不提起,那我们只能在当前点连接到嘴巴半圆的起点多出了没用的线,当然,如果你的这个嘴巴起点是在外面圆上一点,就没什么所谓了。综上,其实moveTo()的作用就相当于我们在画画时提笔换下笔位置的作用是一样的。
然后就是translate了,translate的作用更霸道一点,相当于变换原点,啥意思呢,还是比如你在画画,一开始从左上角画,接下来你要画右下角的东西了,但是你懒,自己希望保持当前优雅的姿势不想动,可是现在直接这么画又太费劲,那咋整,把纸挪一下,让右下角的位置到你面前就好啦,translate()的作用就是这个,变换原点是啥意思呢,就是一开始默认都以(0,0)作为原点,不管是moveTo还是直接画圆画矩形都是以(0,0)作为参考点,而translate(),比如说translate(30,40),当前的(30,40)就相当于(0,0),(32,45)此时应该写成(2,5),跟物理里面的相对位置是一样的,懂否?
3、save() restore()
虽然最后明白了是干啥的,但是后来我在练习中save()了好多次而restore()很少,总体来说save()次数并不受限制,但是restore()的次数一定要小于等于save()的次数,先来段代码帮助大家理解
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title>移动,旋转,缩放,变形</title> 6 </head> 7 <body> 8 <canvas id="myCanvas" width="500" height="500"></canvas> 9 <script> 10 var canvas = document.getElementById(‘myCanvas‘); 11 var ctx = canvas.getContext(‘2d‘); 12 13 // save resore 14 ctx.fillRect(0,0,150,150); 15 ctx.save(); 16 17 ctx.fillStyle = "#09f"; 18 ctx.fillRect(15,15,120,120); 19 ctx.save(); 20 21 ctx.fillStyle = "#fff"; 22 ctx.globalAlpha = 0.5; 23 ctx.fillRect(30,30,90,90); 24 25 ctx.restore(); 26 ctx.fillRect(45,45,60,60); 27 28 ctx.restore(); 29 ctx.fillRect(60,60,30,30); 30 </script> 31 </body> 32 </html>
? save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
? restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
save和restore要配和使用(restore可以比save少,但不能多),如果restore调用次数比save多,会引发Error。
好吧,我承认上面的三句话是我摘抄过来的,因为感觉这三句写的真的不错啊。。。然后我们结合我附的代码来理解一下,先看下上面那段代码运行之后的效果
其实矩形1,2,3不使用save()和restore()依然可以被绘制出来,但是4,5是使用了2,1的填充状态绘制出来的。当我们操作canvas时默认是对所有的元素进行操作,但是有时候我们可能只是这一步或是只是这一个路径或图形需要这个操作,完成这个操作之后,我们希望回到之前的状态再进行下一步的绘制,那么这个时候就是restore()出场了,当然我们自己手动恢复也是可以的,比如说画完矩形3之后,填充颜色是白色,透明度是0.5,我们回到上一步则会变成填充颜色是“#09f“,透明度为1,而在绘制矩形4时仅对矩形的坐标和大小进行了重新定义,并没有对填充方式定义,所以矩形4和矩形2的填充方式是相同的,换句话说,我们是想复用矩形2的状态才使用restore(),如果想用新的方式,那么restore()就没什么卵用。所以综上,我认为,可以常save(),万一能用上呢?
别打我,你敢说你写代码不是常save?
关于绘制图形就说这么多,但是并不是只有这么多,我只是挑一些当时让我有些迷惑的内容来讲一下,更多的内容大家自己摸索就好。
下面说下。。。嗯 ,引用img
好像没啥可说。。。看教程就没啥问题了。。。。
还有一些其他酷炫的效果,比如什么旋转移动变形缩放啥的,还有什么组合啊之类的还有很多,但是我。。。。懒得写了。。。。其实基本按照教程来就没问题的
附个教程链接https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial
如果以后在项目中应用了,会再补充使用体验,请期待吧~