标签:canvas 简单示例 入门知识
作者: 张耀国 ( IgorZhang )
E-mail: igorzhangcn@163.com
canvas 是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形。例如:画图,合成照片,创建动画甚至实时视频处理与渲染。
基本用法
<canvas id="canvas" width="500px" height="500px" >
您的浏览器不支持canvas,请升级您的浏览器!
</canvas>
canvas 标签只有两个属性—— width 和 height。这些都是可选的,当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。宽高只可以通过 JavaScript 进行修改。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲。
如果浏览器版本过低,或者浏览器内核不支持canvas标签的话,那么标签内部的替换内容将会对用户做出提示,若浏览器支持canvas,替换内容将不会出现。
绘制前准备
在绘制之前我们必须先获取canvas元素以及开启2d绘图的渲染上下文(简称获取画笔)
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
代码的第一行通过使用 document.getElementById() 方法来为 canvas 元素得到DOM对象。一旦有了元素对象,你可以通过使用它的getContext() 方法来访问绘画上下文。
栅格
栅格在我们开始画图之前,我们需要了解一下画布栅格(canvas grid)以及坐标空间。如右图所示,canvas元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。所以图中蓝色方形左上角的坐标为距离左边(Y轴)x像素,距离上边(X轴)y像素(坐标为(x,y))。
绘制路径
直线
绘制一条直线,我们需要两个点,即起点和终点。这两个点都将用坐标(x,y)表示。
// 获取元素
var canvas = document.getElementById("canvas");
// 获取上下文对象
// 获取“画笔”,大多数操作都是使用画笔进行
var ctx = canvas.getContext("2d");
// 画一条直线
// 1.开始绘制
ctx.beginPath();
// 2,放置起始点
ctx.moveTo(100, 100);
// 3.放置后续的点 (可以写任意多个)
ctx.lineTo(200, 200);
// 4.结束路径绘制(闭合路径) (非必需)
ctx.closePath();
// 5.画线
ctx.stroke();
此时,我们已经画出了一条直线
我们这里要注意了:
ctx.closePath(); 使用这个方法之后,会自动将起点与终点链接起来,如果不需要连接的话,可以不用写这个方法。但是如果要绘制第二个图形的话,第二个图形必须先使用ctx.beginPath(); 方法,防止与上个图形连接。
绘制一个三角形
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 1.开始绘制
ctx.beginPath();
// 2.放置起始点
ctx.moveTo(75,50);
// 3.放置后续两个点
ctx.lineTo(100,75);
ctx.lineTo(100,25);
// 4.闭合路径
ctx.closePath();
// 5.画线
ctx.stroke();
我们通过获取三个点之后用ctx.stroke()方法进行连接。就可以得到一个三角形。但是:我们此时绘制的仅仅是三条细长的直线连接。有没有什么方法可以修改它的样式呢? 有的,这里我们只需要设置几个属性即可。
// 设置线条宽度
ctx.lineWidth = 20;
// 设置线条颜色
ctx.strokeStyle = "red";
// 设置填充颜色
ctx.fillStyle = "brown";
// 设置线两端为圆角
// 在使用lineCap的时候,一定要非常小心closePath
ctx.lineCap = "round";
// 设置交叉线的交接为圆角
ctx.lineJoin = "round";
这里我们要介绍另一个方法了:fill(); (填充)
fill ( ) 方法能够将绘制的图形进行填充。在刚刚我们绘制三角形时,如果使用 fill ( ) 方法,我们将会获得一个经过颜色填充的三角形。那么我们可以 stroke()和 fill()两个方法同时使用吗? 没问题,这是可以的。
综合实例:绘制一个边框宽度为20像素,边框颜色为红色,填充颜色为黄色的三角形
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ct.lineWidth = 20;
ctx.strokeStyle = "red";
ctx.stroke();
ctx.fillStyle = "yellow";
ctx.fill();
绘制矩形
绘制矩形,canvas 给我们提供了非常便捷的方法:
绘制一个矩形边框
strokeRect(x, y, width, height);
绘制一个填充后的矩形
fillRect(x, y, width, height);
擦除指定大小的矩形区域(可以理解为橡皮擦)
clearRect(x, y, width, height);
x , y 是相对于canvas左上角(原点)的坐标,同样。我们要绘制的矩形也将以 x, y为原点进行绘制。 width 和 height 即为我们要绘制矩形的宽和高。
- 简单实例:
ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
//fillRect()函数绘制了一个边长为100px的黑色正方形。clearRect()函数从正方形的中心开始擦除了一个60*60px的正方形,接着strokeRect()在清除区域内生成一个50*50的正方形边框。
实现效果:
综合实例:绘制一个长宽为200px,边框为10px红色,填充颜色为绿色的,且中间部分矩形区域透明的矩形
ctx.fillStyle = "green";
ctx.lineWidth = "10px";
ctx.strokeStyle = "red";
ctx.strokeRect( 100, 100, 200, 200);
ctx.strokeRect( 100, 100, 200, 200);
ctx.clearRect( 150, 150, 100, 100);
绘制圆和圆弧
绘制圆弧或者圆,我们使用arc()方法。
arc(x, y, radius, startAngle, endAngle, anticlockwise)
画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
arcTo(x1, y1, x2, y2, radius)
根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。
该方法有五个参数:x,y为绘制圆弧所在圆上的圆心坐标。radius为半径。startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。
注意:arc()函数中的角度单位是弧度,不是度数。角度与弧度的js表达式:radians=(Math.PI/180)*degrees。
小例子:绘制笑脸
// 笑脸
ctx.beginPath();
ctx.arc(250,430,70, 0,Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.arc(250,430,50, 0,Math.PI, false);
ctx.stroke();
ctx.beginPath();
ctx.arc(230,410,10, 0,Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.arc(270,410,10, 0,Math.PI*2, true);
ctx.stroke();
小例子:绘制一个绘制一个由多个随机颜色不同大小的圆组成的镖靶
// 定义一个函数,用于获取随机颜色
function random () {
return parseInt(Math.random() * 255);
}
// 用for循环创建10个填充了随机颜色的圆形
for( var i = 10; i > 0 ; i--){
ctx.beginPath();
ctx.arc(200, 200, 20 * i, 0, Math.PI * 2, true);
ctx.fillStyle = "rgb(" + random() + "," + random() + "," +random() + ")";
ctx.fill();
ctx.stroke();
}
绘制二次贝塞尔曲线及三次贝塞尔曲线
贝塞尔曲线
在绘制图形时,贝塞尔全非常有用,常用来绘制复杂的有规律的图形。
quadraticCurveTo(cpx, cpy, x, y)
绘制二次贝塞尔曲线,cpx,cpy表示控制点的坐标, x,y表示终点坐标;
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
上边的图能够很好的描述两者的关系,贝塞尔曲线有一个开始、结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线使用两个控制点。
参数x、y在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。
二次贝塞尔曲线
quadraticCurveTo(cpx, cpy, x, y)
cpx,cpy表示控制点的坐标, x,y表示终点坐标;
P0 为起点, P2为终点, P1为控制点。
// 先简单绘制一个二次贝塞尔曲线
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
//绘制起始点、控制点、终点
context.beginPath();
context.moveTo(20,170);
context.lineTo(130,40);
context.lineTo(180,150);
context.stroke();
//绘制二次贝塞尔曲线
context.beginPath();
context.moveTo(20,170);
context.quadraticCurveTo(130,40,180,150);
context.strokeStyle = "red";
context.stroke();
效果如图:
实例: 用二次贝塞尔曲线绘制对话气泡
// 二次贝尔赛曲线
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();
代码效果如下:
三次贝塞尔曲线
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
P0 和 P3 为起点和终点, P1 和 P2 为控制点
先简单绘制一个三次贝塞尔曲线模型感受一下。
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
//绘制起始点、控制点、终点
context.beginPath();
context.moveTo(25,175);
context.lineTo(60,80);
context.lineTo(150,30);
context.lineTo(170,150);
context.stroke();
//绘制3次贝塞尔曲线
context.beginPath();
context.moveTo(25,175);
context.bezierCurveTo(60,80,150,30,170,150);
context.strokeStyle = "red";
context.stroke();
效果图如下:
实例:使用三次贝塞尔曲线绘制一个心形❤
// 三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();
代码效果如下:
绘制文本
canvas 提供了两种方法来渲染文本:
fillText(text, x, y [, maxWidth])
在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的.
strokeText(text, x, y [, maxWidth])
在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的
上代码:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 获取canvas的宽度和高度
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
// 设置字体
ctx.font = "50px 宋体"
// 设置水平对其方式
// 水平对齐选项. 可选的值包括:start, end, left, right or center. 默认值是 start。
ctx.textAlign = "center";
// 设置垂直对齐方式
// 基线(垂直)对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
ctx.textBaseline = "middle"
// 绘制文字
ctx.storkeText("今天要是啊",100,100);
// 让文字水平垂直居中
ctx.font = "40px 楷体"
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("哈哈哈", canvasWidth/2, canvasHeight/2);
文本同样可以使用 strokeText() 和 fillText() 来绘制空心或实心文字,并且可以添加各种样式
使用图片
从零创建一个图片
我们可以用脚本创建一个新的Image对象。要实现这个方法,我们可以使用很方便的Image()构造函数。
var img = new Image(); // 创建一个<img>元素
img.src = 'myImage.png'; // 设置图片源地址
当脚本执行后,图片开始装载。
若调用 drawImage 时,图片没装载完,那什么都不会发生(在一些旧的浏览器中可能会抛出异常)。因此你应该用load时间来保证不会在加载完毕之前使用这个图片:
var img = new Image(); // 创建img元素
img.src = 'myImage.png'; // 设置图片源地址
// 当图片加载完毕之后,才把图片绘制到canvas中
img.onload = function(){
// 执行drawImage语句
ctx.drawImage(img, 100, 100);
}
img.src = 'myImage.png'; // 设置图片源地址
使用视频帧
你还可以使用<video> 中的视频帧(即便视频是不可见的)。例如,如果你有一个ID为“myvideo”的<video> 元素,你可以这样做:
function getMyVideo() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
return document.getElementById('myvideo');
}
}
它将为这个视频返回HTMLVideoElement对象,抓取当前视频帧作为一个图像。
形变
旋转、位移、缩放
旋转:
ctx.rotate(deg);
以坐标原点进行 deg 角度的旋转,可以为负值
位移:
ctx.translate(x, y);
以坐标原点进行横向(x 像素),纵向(y 像素)的位移,可以为负值
缩放:
ctx.scale(x, y);
以坐标原点进行横向(x 倍数),纵向(y 倍数)的缩放。
综合实例: 利用一个综合性的小实例进行学习(制作一个表盘)
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 获取canvas的宽度和高度
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
// 将坐标原点移动至画布中心
ctx.translate(canvasWidth/2, canvasHeight/2);
// 设置线的两端为圆角
ctx.lineCap = "round";
// 设置线条宽度为10px
ctx.lineWidth = 10;
// 用foru循环绘制表盘的时间刻度
for(var i = 0; i < 12; i++) {
ctx.beginPath();
// 绘制一条长度为20的线
ctx.moveTo(0,-200);
ctx.lineTo(0, -180);
ctx.stroke();
// 绘制完成后将坐标原点旋转30度
ctx.rotate(Math.PI / 6);
}
建议将代码拷贝到你自己的编辑器中多进行修改,可更快的了解每个方法和属性的作用。祝你学习愉快。
网友评论