canvas画布绘制动画基础

目录
文章目录隐藏
  1. canvas 绘制基础
  2. 结束语

今天给大家带来新的 canvas 绘制绚丽的倒计时动画效果制作方法以及 canvas 绘图动画基础知识,这篇文章将带领大家逐步学习最激动人心的基础 canvas,canvas 顾名思义是定义在浏览器上的画布,通过我们后面的介绍大家会慢慢的明白,canvas 并不像 p 标签、h 标签、img 标签一样的元素,canvas 更是一套编程工具,是一套编程接口。它的出现已然超过了 web 给予文档设计的初衷,将网页这一形态的工具推向了更高的高度,利用它我们可以开发出很多梦寐以求的东西,比如说游戏、动画、动态图表和更富有表现力的感染力的应用,你可以开发出一个灯光秀,一个物理模拟引擎或者是更加绚丽的 3D 效果,canvas 为我们敞开了一扇新的大门。我很喜欢一个说法,过去 web 编程就像是软件开发的黑暗时代,因为我们只是简单把数据从数据库拿出来显示在网页上,但是 canvas 就像是一场文艺复兴,让编程人员彻底的释放自己的创造力。之前写过一篇类似文章《HTML5 Canvas 画布》,今天这篇文章是加强版,文章没有太难得知识点,只是一些基础的东西,比如球型的绘制,动画的基本原理。大家通过本篇文章将会体会到一些极其基础的 canvas 内容,已经可以帮助我们制作出绚丽的效果。后面我将会逐渐深入到 canvas 的方方面面,力图每一个课程除了介绍知识以外还能帮助大家切实使用 canvas 制作出绚丽的 demo 应用在自己的产品中,当然更希望大家在掌握了 canvas 后,制作出属于自己的动画游戏作品,一起来看一下今天的内容。

canvas 绘制基础

创建 Canvas

canvas 是 HTML 中的一个新元素,本身和其它元素区别并不大,就是一个 canvas 标签,我们在浏览器中插入一个 canvas 标签,通常还会配一个 ID,方便我们在 JavaScript 中获取 canvas 元素。

<canvas id="myCanvas"></canvas>

此时我们运行时页面上是一片空白,因为里面没有任何的内容,我们可以向其他 HTML 元素一样为它写上样式

<canvas id="canvas" style="border:1px solid #aaa;display:block;margin:50px auto;"></canvas>

我们为它添加 1 个像素的边框,显示方式是 block,并调整它的位置,如下图:

创建 canvas
当我们未指定 canvas 元素宽高时默认是 300 像素宽,150 像素高,如果我们想要定义宽高,如何写呢?我们直接在 canvas 标签下调用它的 width 和 height 属性:

<canvas id="canvas" width="1024" height="768" style="border:1px solid #aaa;display:block;margin:50px auto;"></canvas>

这里需要大家注意两点:
第一点是不建议大家使用 CSS 的方式为 canvas 指定大小,这是因为 CSS 指定的是 canvas 这块画布显示的大小,canvas 当然还包括显示的内里的分辨率大小,采用这种方式相当于一同把这个大小给制定出来,符合 W3C 标准的;
第二点是我们可以看到给出的 width 和 height 属性是没有添加单位的,这里大家要注意一点,原因其实和上面一样,这里的大小也决定了 canvas 画布内里的元素精度,W3C 规定应该这样制定 canvas 大小。以后大家学会了如何在 canvas 画布回话以后,可以试一下区别。

当我们创建了 canvas 如何在画布上绘制呢?

在 JavaScript 中有两步进行操作;

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");//使用 context 进行绘制

定义变量 canvas 通过 document.getElementById()方法取出刚才我们 ID,然后调用 canvas 中 getContext()方法得到一个上下文的环境。我用 context 变量来表示它,这个 context 变量定义的上下文环境才是我们真正要绘制的一个接口,在这个 getContext()方法里我们传入了一个字符串的参数叫”2d”,这里我们都是以 2 的绘制图为主,后续我们有机会摄入 3d 方面知识。

这两部初始化以后,我们就可以使用 context 进行绘制了,如下代码:

window.onload = {
    var canvas = getElementById("canvas");
    var context = canvas.getContext("2d");
}

这里我还想说明一点,canvas 除了可以调用 getContext()方法外,还有一个很重要的方法,可以再 JavaScript 层面中决定 canvas 大小,那就是:

canvas.width = 1024;
canvas.height = 768;

这样呢也可以指定 canvas 大小,就不用再 canvas 标签中定义了,另外,提及一些兼容性的话题,事实上现在大部分主流浏览器都是支持 canvas 绘图的,但也保不齐一些老用户使用比较老的浏览器,那么一个可选的处理方式是在 canvas 标签内写上当用户的浏览器不支持 canvas 绘图时,提醒对方看到什么“当前浏览器不支持 canvas,请更换浏览器重试”,这只是一个例子,当然我们可以在 canvas 添加更复杂的样式,但是大家要注意,当用户浏览器支持 canvas,标签内的提示文字是完全被忽略掉的

<canvas id="canvas" style="border:1px solid #aaa;display:block;margin:50px auto;">
当前浏览器不支持 canvas,请更换浏览器重试!
</canvas>

当然我们还可以利用在 JavaScript 中利用一个 if 语句判断浏览器是否支持 canvas,具体的方法像这样:

if(canvas.getContext("2d")){
    var context = canvas.getContext("2d");
    //使用 context 绘制
}else{
    alert("当前浏览器不支持 canvas,请更换浏览器重试!")
}

我们总结一下:

我们在拿到 canvas 后,真正使用到的呢只有 canvas 的三个方法

canvas.width= 1024;
canvas.height = 768;
canvas.getContext("2d");

绘制直线、多边形、七巧板

我们从绘制一条直线开始,看看 canvas 是如何来进行具体的绘制的,通过以下三个方法就可以画一条直线:

//draw a line
context.moveTo(100,100);
context.lineTo(700,700);

context.stroke();

大家可以想象,一个人手里拿一支笔,第一行代码 contex.moveTo(100,100)就是这个人将笔尖放到了(100,100)这个点的位置上;那么第二行代码 context.lineTo(700,700)就是这个人的笔从(100,100)一直走到了(700,700),但是这里注意这两行代码的意思知识这个人一图画一条(100,100)到(700,700)的线,那么具体的绘制就要调用一个 context.stroke()这样一个函数,那么这三行代码合在一起就绘制成了一条(100,100)到(700,700)的直线。

那么说到这里大家要明白一个概念,canvas 中绘图是一种基于状态的绘图,也就是整个绘图过程先设置一个绘图的状态,之后调用具体的函数来做一个具体的绘制,比如这个例子中的:

context.moveTo(100,100);
context.lineTo(700,700);

就是一个状态设置,我要绘制一条(100,100)到(700,700)的直线,而 context,stroke()函数就是进行具体的绘制。

我们在代码编辑器中将三行代码写入保存看一下具体的效果展示:

window.onload = function() {
	var canvas = document.getElementById("canvas");
	
	canvas.width = 1024;
	canvas,height = 768;
	
	if(canvas.getContext("2d")){
		var context = canvas.getContext("2d");
		//使用 context 绘制
		context.moveTo(100,100);
		context.lineTo(700,700);
		context.stroke();
	}else{
		alert("当前浏览器不支持 canvas,请更换浏览器重试");
	}
}

context 绘制直线

上图就是这个我们在创建的 1024*768 的画布中绘制的一条(100,100)到(700,700)的直线效果。这里大家要注意在画布系统中坐标系的设定是以左上角为原点向右为 X 轴正方向,向下为 Y 轴的正方向。

这里我们再补充两个绘制状态:

context.lineWidth = 5;
context.strokeStyle = "#005588";

context.lineWidth 是设置线条的宽度,context.strokeStyle 描述线条样式,这个样式主要是指颜色,这个颜色可以使用 CSS 方法中任意方法类赋值,但是要放到一个字符串中,此时运行效果。

canvas 绘制线条宽和颜色

说到这里小伙伴可能有疑问了,如果有多个线段该怎么绘制呢?很简单,相应的我们接一个 context.lineTo(100,700)就可以,效果如下:

canvas 如何对多个线段绘制

大家自然可以想象,只要最后这个 lineTo 与最初的 moveTo 的坐标点一样,这样的就可达到首尾衔接形成一个多边形,看一下效果:

canvas 如何绘制多边形效果

这样计算出多边形的每个顶点位置并且顺序的用 lineTo 连接起来就可以绘制出任何多边形,矩形、梯形,甚至一个小星星都不在话下。

如何为我们形成的这个多边形进行着色呢?这里要介绍一个新的状态 fillStyle 和绘制方法 fill();

context.fillStyle = "rgb(2,100,30)"
context.fill()

效果展示

fill 为多边形填充颜色

当然我们可以将 fill()填充方法和 stroke()方法结合使用

window.onload = function() {
	var canvas = document.getElementById("canvas");
	
	canvas.width = 1024;
	canvas,height = 768;
	
	if(canvas.getContext("2d")){
		var context = canvas.getContext("2d");
		//使用 context 绘制
		context.moveTo(100,100);
		context.lineTo(700,700);
		context.lineTo(100,700);
		context.lineTo(100,100);
		
		context.fillStyle = "rgb(2,100,30)";
		context.fill();
		
		context.lineWidth = 5;
		context.strokeStyle = "red";
		context.stroke();
	}else{
		alert("当前浏览器不支持 canvas,请更换浏览器重试");
	}
}

canvas 填充描边

如果我们想要多个路径分开处理需要队首尾使用 context.beginPath()和 context.closePath()来处理。

接下来我们用上面学到的方法在 canvas 画布上绘制一个七巧板

canvas 画布上绘制一个七巧板

我们定义一个 tangram 变量,这个变量是个数组有七部分用来存放七巧板数据,每一部分是一个类的对象,具体包括:一个 P 标签里面也是一个数组,数组内是每一块的顶点坐标,最后还有一个 color 属性,代表这一块使用的颜色。

var tangram = [
	{p:[{x:0,y:0},{x:800,y:0},{x:400,y:400}],color:"#caff67"},
	{p:[{x:0,y:0},{x:400,y:400},{x:0,y:800}],color:"#67becf"},
	{p:[{x:800,y:0},{x:800,y:400},{x:600,y:600},{x:600,y:200}],color:"#ef3d61"},
	{p:[{x:600,y:200},{x:600,y:600},{x:400,y:400}],color:"#f9f51a"},
	{p:[{x:400,y:400},{x:600,y:600},{x:400,y:800},{x:200,y:600}],color:"#a594c0"},
	{p:[{x:200,y:600},{x:400,y:800},{x:0,y:800}],color:"#fa8ecc"},
	{p:[{x:800,y:400},{x:800,y:800},{x:400,y:800}],color:"#f6ca29"},
]

同样我在 window.onload 中通过 ID 获取 canvas 元素,定义宽高为 800,取得 canvas 的 context,最后我通过一层 for 循环把我定义的七巧板变量数组进行一次遍历,每一次遍历都调用我下面定义的 draw 函数。draw 函数将传入两个参数,第一个参数为七巧板中的一块,第二个参数为我们绘制图的上下文环境 context。这个 context 是非常重要的,要相互自会离不开他。

window.onload = function() {
	var canvas = document.getElementById("canvas");
	
	canvas.width = 800;
	canvas,height = 800;
	var context = canvas.getContext("2d");
	//使用 context 绘制
	for(var i = 0; i < tangram.length; i++){
		draw(tangram[i],context);
	}
}

接下来我们看看 draw 函数方法,draw 函数包含两个参数一个是 piece,一个是上下文环境简写成 cxt,那么在绘制每一个 piece 中我首先 cxt.beginPath(),然后使用 cxt.moveTo()方法挪到 piece 这一小块的第一个顶点坐标的位置,其次使用一个循环顺次的使用这个 cxt.lineTo()绘制他们后续的几个坐标,最后 cxt.closePath()表示绘制路径完毕。之后我用 cxt.fillStyle 调用 piece.color 这个定义好的颜色,然后调用 fill()方法将这一小块绘制出来。方法如下:

function draw(piece, cxt) {
	cxt.beginPath();
	cxt.moveTo(piece.p[0].x, piece.p[0].y);
	for(var i = 1; i < piece.p.length; i++){
		cxt.lineTo(piece.p[i].x, piece.p[i].y);
	}
	cxt.closePath();
	cxt.fillStyle = piece.color;
	cxt.fill();
	
	cxt.strokeStyle = "block";
	cxt.lineWidth = 3;
	cxt.stroke();
}

是不是很简单,这些都是上面学的知识,大家就可以轻松制作出绚丽的效果了,根据之前我们学过想要为每小块七巧板的外边框得到绘制的话,怎么实现呢?我们可以加入这样一段代码:

cxt.strokeStyle = "block";
cxt.lineWidth = 3;
cxt.stroke();

说到这里如果大家 JS 很牛掰的话,完全可以制作一个七巧板小游戏了,可以使鼠标移动每一小块进而组装成不同的图案,这里只是抛砖引玉,感兴趣的小伙伴可以下去动手实现一下。

绘制弧和圆

上面的内容我们讲了绘制直线,接下来看一下 canvas 中是如何绘制弧线呢?在 canvas 绘制弧线有一个 arc()方法,arc() 方法创建弧/曲线(用于创建圆或部分圆)这个方法需要传入的参数比较多,有六个,但是也好记忆

context.arc(
  centerx, centery, radius,
  startingAngle, endingAngle,
  anticlockwise = false
)

centerx 表示圆的中心的 x 坐标

centery 表示圆的中心的 y 坐标

radius 表示圆的半径

startingAngle 表示起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)

endingAngle 结束角,以弧度计

anticlockwise 可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。

提示:如需通过 arc() 来创建圆,请把起始角设置为 0,结束角设置为 2*Math.PI。

提示:请使用 stroke() 或 fill() 方法在画布上绘制实际的弧。

绘制弧和圆

  • 中心:arc(100,75,50,0*Math.PI,1.5*Math.PI)
  • 起始角:arc(100,75,50,0,1.5*Math.PI)
  • 结束角:arc(100,75,50,0*Math.PI,1.5*Math.PI)

代码例子:

window.onload = function() {
  var canvas = document.getElementById("canvas");
	
  canvas.width = 800;
  canvas.height = 800;
  var context = canvas.getContext("2d");
  //使用 context 绘制
  context.lineWidth = 5;
  context.strokStyle = "#005588";
  context.arc(300,300,200,0,1.5*Math.PI);
  context.stroke()
}

同样的方式,通过 ID 获取 canvas 元素,定义 canvas 的宽高,利用 canvas 获取 context,context.lineStyle 给它设置线宽,context.strokeStyle 设置弧线的颜色,利用 context.arc()方法定义以 300,300 为原点,200 为半径,0 为起始角度,1.5*Math.PI 结束。这里的 arc 函数也是状态函数,描述我们将要绘制的状态,之后再调一下 stroke。

效果

arc 绘制弧和圆

当然我们第六个参数没有写,默认是从顺时针绘制,当然你也可以尝试将第六个参数付一个 true。

context.arc(300, 300, 200, 0, 1.5*Math.PI, true);

看一下效果

arc 顺时针绘制

接下来我们逆时针从 0 绘制到 0.5*Math.PI 结束

context.arc(300, 300, 200, 0, 0.5*Math.PI, true);

arc 逆时针绘制 0.5PI

希望大家在体会一下我说的,无论是顺时针还是逆时针,0、0.5、1、1.5 他们的位置是不变的。

这是绘制一段弧,如果我们绘制多段弧的话会怎么样呢,大家看一下这段代码:

window.onload = function() {
  var canvas = document.getElementById("canvas");
	
  canvas.width = 1024;
  canvas,height = 768;
  var context = canvas.getContext("2d");
  //使用 context 绘制
  context.lineWidth = 5;
  context.strokStyle = "#005588";
  //第一段
  for(var i = 0; i < 10; i++){
    context.beginPath();
    context.arc(50 + i*100,60,40,0,2*Math.PI*(i+1)/10);
    context.closePath();
    context.stroke();
  }
}

我用 for 循环绘制了十段弧,大家想想我们上面说到的要使用到 context.beginPath()和 context.closePath()了,把每段弧的定义给包起来,在弧的定义里呢,我使用变量"i"不断地改变圆心的坐标位置,同事保持 startingAngle 是不变的,到达的位置也是根据"i"不断地变化,效果如下:

arc 连续绘制是个弧

那么这个显示的结果可能和大家想象的不同,最大的不同就是在这个弧的起点和终点位置被一条线段连接起来,这是什么鬼?其实这也是我们之前讲过的这个 closePath()的另外一个作用,当我们当前绘制状态的路径不是封闭路径的时候,如果我们使用了 closePath(),closePath()会自动为我们将这段不封闭的路径的首尾使用一条线段连接起来,那么大家可能要问了我如何绘制多个弧,而这个弧没有那条线段让他们首尾相连呢?那么我在原有代码基础下写下这样一段代码:

window.onload = function() {
	var canvas = document.getElementById("canvas");
	
	canvas.width = 1024;
	canvas,height = 768;
	var context = canvas.getContext("2d");
	//使用 context 绘制
	context.lineWidth = 5;
	context.strokStyle = "#005588";
	//第一段
	for(var i = 0; i < 10; i++){
		context.beginPath();
		context.arc(50 + i*100,60,40,0,2*Math.PI*(i+1)/10);
		context.closePath();
		context.stroke();
	}
	//第二段
	for(var i = 0; i < 10; i++){
		context.beginPath();
		context.arc(50 + i*100,180,40,0,2*Math.PI*(i+1)/10);
		context.stroke();
	}
}

区别是我这段代码只是用了 beginPath()表面我要开始这段路径,而没有使用 closePath()结束这段路径,这样就不会有那条线段首尾相连,与此同时,在下一次开始会只是又一次调用 beginPath(),那么 canvas 知道我们又要从新规划路径不影响我们绘制多个路径,来看一下效果:

arc 绘制多个弧首尾不相连

 

这是我们期望的一个结果,通过这个例子相信大家有深入的了解了一下 closePath()和 beginPath(),而 closePath()和 beginPath()不一定要成对出现,beginPath()代表要重新规划一段路径,closePath()代表要结束当前的路径,如果当前路径没有封闭上的话会自动让当前的路径封闭上。

接下里我又写入一段代码:

window.onload = function() {
	var canvas = document.getElementById("canvas");
	
	canvas.width = 1024;
	canvas,height = 768;
	var context = canvas.getContext("2d");
	//使用 context 绘制
	context.lineWidth = 5;
	context.strokStyle = "#005588";
	//第一段
	for(var i = 0; i < 10; i++){
		context.beginPath();
		context.arc(50 + i*100,60,40,0,2*Math.PI*(i+1)/10);
		context.closePath();
		context.stroke();
	}
	//第二段
	for(var i = 0; i < 10; i++){
		context.beginPath();
		context.arc(50 + i*100,180,40,0,2*Math.PI*(i+1)/10);
		context.stroke();
	}
	//第三段
	for(var i = 0; i < 10; i++){
		context.beginPath();
		context.arc(50 + i*100,300,40,0,2*Math.PI*(i+1)/10, true);
		context.closePath();
		context.stroke();
	}
}

在第三段 for 循环中,我依然用 closePath()和 beginPath()包住了 arc(),同时 arc 的最后一个参数传入了 true,表示我要使用逆时针方向一点一点绘制这个弧线,效果如下:

arc 弧线绘制

大家还是要注意以下,即使我使用逆时针方向来绘制,这个结束的弧度的位置是不变的,所以相应的在此时顺时针方向由小到大一点一点画,逆时针从大到小一点一点画直至最后变成一个完整圆,因为我们使用 closePath()来结束这个路径,所以首尾两端也会有一段线段相连。

如果我们去掉 closePath(),相信大家结果已经知道显示什么样子了,说到这里相信大家已对 arc 绘制圆掌握,之前我们绘制的弧包括圆都是一个路径,而不是实心的。那么我们如何进行填充处理呢?

相信聪明的小伙伴已经想到了,那就是要用到我们上面学到的 fill 函数,在这里我做一个演示,我先使用 fillStyle 给它声明一个填充颜色,arc 部分没有变化,依然使用 beginPath()和 closePath()包住 arc,最后调用 fill 函数:

context.fillStyle = "#005588";
for(var i = 0; i < 10; i++){
	context.beginPath();
	context.arc(50 + i*100,540,40,0,2*Math.PI*(i+1)/10);
	context.closePath();
	context.fill();
}

效果如下:

arc 绘制圆填充颜色

写到这里可能有人问了,由于在这个代码里我们使用了 closePath()把这个路径结束了,因此按照之前讲的,应该有一个线段截住这个半弧的情况,此时就可以在这个半弧里进行着色,如果我们不写这个 closePath 会怎样呢?

for(var i = 0; i < 10; i++){
	context.beginPath();
	context.arc(50 + i*100,660,40,0,2*Math.PI*(i+1)/10);
	context.fill();
}

代码和前面一样,只是不使用 closePath()来结束这个路径,我们看一下效果:

去掉 closePath 填充颜色

结果显示不使用 closePath 来结束这个路径效果和使用 closePath 的填充效果一样,这也告诉了我们 closePath 对于 fill 是没有用的,当我们使用 fill 的时候表示要填充,如果要填充就要找一个首尾相连的路径进行填充,那么此时不管你是不是调用了 closePath,当调用 fill 函数的时候,canvas 会自动把没有封闭的路径首尾相连之后进行填充处理。

结束语

canvas 还为我们提供了更多的接口,在后续文章我们会结合案例穿插降解,根据今天所讲的内容我会针对性的带领大家实现一个炫酷的动态倒计时效果,敬请期待!

「点点赞赏,手留余香」

7

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系maynote@foxmail.com处理
码云笔记 » canvas画布绘制动画基础

2 评论

  1. 很详细的教程,楼主好人,哈哈

发表回复