canvas 是html5新增的一个比较强大的功能,简直是一个画图工具一样。
写这篇文章只是方便以后用的时候可以快速上手。
canvas的操作基本是用js来完成的,下面开始讲一些基本用法。
一.最基本的用法
要用画布,先要写出画布标签。
<canvas height="500" width="700" style="border:1px solid black" id="canvas">浏览器不支持canvas</canvas>
标签 包裹内容为当浏览器不支持canvas时显示的内容
这里要注意,定义canvas宽高时建议用标签的属性height、width。经测试,如果在style内设置width,height会使绘图环境(下面会讲什么是绘图环境)缩放,使得绘图环境不好控制。
要在canvas上画画,要用到js
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//开始一个绘画路径
ctx.beginPath();
//描边颜色
ctx.strokeStyle = "red";
ctx.fillStyle = "blue";
ctx.moveTo(20, 20);
ctx.lineTo(50, 50);
ctx.lineTo(40, 20);
//连接回最初的起点
ctx.closePath();
//对路径进行描边
ctx.stroke();
ctx.fill();
</script>
这里用getContext()来获得一个绘图环境。后面就是用这个绘图环境做各种绘图。
beginPath()是标志开始一段新的路径绘画。strokeStyle是定义描线的颜色。fillStyle是定义填充的颜色。moveTo(x, y)是定义路径起点。lineTo(x, y)是画路径。这里之所说是画路径,而不是说画线,是因为lineTo(包括后面说到的rect,arc)只是程序定义了一个作图的路线,并没有在这条路线上做任何动作,所以上面的程序如果没有后面的stroke()或fill(),画布内是不会显示任何东西的。这里一定要理解好,不然后面你会凌乱的。
closePath()是闭合当前路径(并不是关闭),即把当前路径的终点和路径起点连接起来。然后就是上面说到的stroke(),stroke()是对当前的路径进行描边(即画线)。当前路径指的是从beginPath()开始,所以每画一个图形的开头,最好都加上beginPath(),这样可以防止第一个图形的路径终点连接到第二个图形的路径起点。
fill()是填充当前路径的闭合区域。
二.画基本图形
<script>
//矩形
ctx.beginPath();
ctx.fillStyle = "red";
//填充一个矩形(x, y, 宽, 高)
//方法一:
ctx.fillRect(20, 50, 100, 70);
//方法二:
ctx.rect(130, 50, 100, 70);
ctx.fill();
</script>
上面的方法一跟方法二效果是一样的(只有矩形的坐标不同)。fillRect直接生产一个填充了的矩形,而rect是定义了一个矩形的路径,还没有画出来。
在实际应用中,建议先把图形的路径画好了,再去填充或描线。因为描边和填充会消耗大量资源,所以尽量把路径都弄好一次过描边或填充。
<script>
//圆形
ctx.beginPath();
ctx.fillStyle = "red";
//圆形(x,y,半径,起始角,结束角,绘图方向:true逆时针,false顺时针)
ctx.arc(40,220,50,0,2*Math.PI,false);
ctx.fill();
</script>
arc是一个画圆弧的方法,参数的介绍代码中已经有了。
<script>
//椭圆形
ctx.beginPath();
ctx.save();
ctx.scale(1, 0.5);
ctx.fillStyle = "blue";
ctx.arc(240,360,50,0,2*Math.PI,false);
ctx.fill();
ctx.restore();
</script>
没有直接画椭圆的方法,上面代码是通过把一个圆给压缩得到一个椭圆。
save()是保存当前画面的各种状态包括画笔颜色、填充颜色、画面旋转角度、画面基点坐标、画面缩放等……。restore()则是恢复到save()保存的状态。代码中可以出现多个save(),restore()是把画面恢复到离它最近的一个save()的状态。
注意这里我说的是画面,而不是画布。画布即canvas标签,这里的画面指的是getContext("2d")对象,即上面说的绘图环境。
scale(x, y)是对画面进行缩放,后面再来介绍。
三.绘图环境的缩放、旋转、基点坐标改变。
1.改变基点(translate)
先说一下基点坐标。默认的基点坐标是(0, 0),即画布的左上角坐标。上面说到的rect()方法中的前两个参数x, y就是相对于基点坐标(0, 0)而言的。
<script>
//改变基点坐标
ctx.beginPath();
ctx.save();
ctx.rect(300,300,100,90);
ctx.translate(20,20);
ctx.rect(300,300,100,90);
ctx.stroke();
ctx.restore();
</script>
可以看到,上面两个rect()的参数是一样的,但是画出来的矩形却不在同一位置上。因为第二个矩形在定义路径之前,绘图环境的基点坐标被改变了,所以第二个rect的前两个参数是基于新的基点坐标来定位。
注意,这里是先改变基点坐标,再去画路径。包括后面的缩放和旋转都是要先改变,再画路径。
(从这里我猜测每次画路径,系统都会去读一次绘图环境的各种参数。这个我还没去研究。)
2.缩放(scale)
<script>
//绘图环境缩放
ctx.beginPath();
ctx.save();
ctx.strokeStyle = "green";
ctx.strokeRect(20, 20, 25, 15);
ctx.scale(2,2);
ctx.strokeRect(20, 20, 25, 15);
ctx.scale(2,2);
ctx.strokeRect(20, 20, 25, 15);
ctx.restore();
</script>
这是直接从W3C那边复制过来的代码。如果对绘图环境进行缩放,所有之后的操作也会被缩放。定位也会被缩放。
3.旋转(rotate)
<script>
//旋转(基于基点来旋转)
ctx.beginPath();
ctx.save();
ctx.strokeRect(20, 20, 170, 120);
ctx.rotate(Math.PI/5);
ctx.strokeRect(20, 20, 170, 120);
ctx.restore();
</script>
就像代码中注释的,旋转是基于基点来旋转的。改变基点后旋转的效果是不同的。
四.填充渐变色
<script>
//线性渐变(起点之前是起点色,终点之后是终点色)
var linear = ctx.createLinearGradient(200,200,350,330);
linear.addColorStop(0,"#fff");
linear.addColorStop(0.5,"#f8f");
linear.addColorStop(1,"#f0f");
ctx.fillStyle = linear;
ctx.fillRect(200,200,150,130);
//径向渐变
ctx.beginPath();
var radial = ctx.createRadialGradient(200,480,10,200,450,50);
radial.addColorStop(0,"#fff");
radial.addColorStop(0.5,"#f8f");
radial.addColorStop(1,"#f0f");
ctx.fillStyle = radial;
ctx.arc(200,450,50,0,Math.PI*2,0);
ctx.fill();
</script>
渐变填充分线性渐变和径向渐变。
createLinearGradient(x1, y1, x2, y2)创建一个线性渐变对象,(x1, y1)和(x2, y2)连线部分就是渐变色区域。(这里很难表达清楚,看效果更容易理解)
addColorStop()是在区域中添加渐变色。第一个参数在0~1范围之间,表示在线段的哪个位置加渐变色。
设置好渐变对象后记得把渐变对象赋值给填充状态就好了。
createRadialGradient(x1, y1, r1, x2, y2, r2)创建径向渐变对象,所谓径向就像水上的涟漪,由圆心往外扩展。(x1, y1, r1)和(x2, y2, r2)分别表示两个圆,渐变色就是从第一个圆开始颜色渐变到第二个圆。
渐变色这部分很难解释清楚,多用一下就好理解了。
最后附上时钟代码
<html>
<canvas height="500" width="500" style="border:1px solid black;position:absolute;" id="clock_bg"></canvas>
<canvas height="500" width="500" style="border:1px solid black;position:absolute;" id="clock_point"></canvas>
<script>
var ctx1 = document.getElementById("clock_bg").getContext("2d");
var ctx2 = document.getElementById("clock_point").getContext("2d");
//画钟背景
function print_bg(){
ctx1.save();
ctx1.beginPath();
ctx1.translate(200,200);
ctx1.arc(0, 0, 2, 0, 2*Math.PI, 0);
ctx1.fill();
ctx1.beginPath();
ctx1.arc(0, 0, 105, 0, 2*Math.PI, 0);
var len = 0;
for(var i=1;i<=60;i++)
{
ctx1.save();
ctx1.rotate(2*Math.PI*i/60);
if(i%5){
len = 5;
}else{
len = 10;
ctx1.fillText(i, -5, -110);
}
ctx1.moveTo(100, 0);
ctx1.lineTo(100-len, 0);
ctx1.restore();
}
ctx1.stroke();
ctx1.restore();
}
//移动指针
function move_point(){
var n_date = new Date();
var hour = n_date.getHours();
var min = n_date.getMinutes();
var sec = n_date.getSeconds();
with(ctx2){
clearRect(0,0,500,500);
save();
translate(200, 200);
save();
//时针
beginPath();
lineWidth = 3;
rotate(2*Math.PI*hour/12);
moveTo(0, 0);
lineTo(0, -45);
stroke();
restore();
save();
//分针
beginPath();
lineWidth = 2;
rotate(2*Math.PI*min/60);
moveTo(0, 0);
lineTo(0, -60);
stroke();
restore();
save();
//秒针
beginPath();
rotate(2*Math.PI*sec/60);
moveTo(0, 0);
lineTo(0, -70);
stroke();
restore();
restore();
}
}
print_bg();
move_point();
setInterval(function(){move_point()},1000);
</script>
</html>