企业网站,个人博客等WordPress网站以及其他语言网站开发定制需求加QQ详聊。

canvas画布绘制动画基础

HTML CSS 码云 97℃ 0评论
目录
[隐藏]

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

转载请注明:码云笔记 » canvas画布绘制动画基础

喜欢 (5)or分享 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(2)个小伙伴在吐槽
  1. 很详细的教程,楼主好人,哈哈
    青菜萝卜干2018-12-04 07:27 回复