一. 概述
canvas又称为"画布",是HTML5的核心技术之一.我们常说的Canvas技术,指的就是使用Canvas元素集合Javascript来绘制各种图形的技术。
canvas的作用:
1. 绘制图形
canvas可以用来绘制各种基本图形如矩形、曲线、圆等,也可以绘制各种复杂绚丽的图形。
2. 绘制图表
很多公司业务的数据展示都离不开图表,使用Canvas可以用来绘制满足各种需求的图表。
3.动画效果
使用Canvas,我们也可以制作出各种华丽的动画效果
4. 游戏开发
简单的说,HTML5 canvas,就是一门使用JavaScript来操作Canvas元素的技术。使用Canvas元素来绘制图形,需要以下三步。
(1)获取Canvas对象
(2)获取上下文环境对象context
(3) 开始绘制图形
注意:Canvas使用的是W3C坐标系,而不是数学坐标系。其中,W3C坐标系的Y轴正方向向上。如下:
二. 直线 曲线图形
直线图形
直线、三角形、矩形的绘制如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="500" height="600" style="border:1px dashed gray;"></canvas>
<script>
// 1. 找到画布
let cnv=document.getElementById('canvas')
// 2. 获取画笔,上下文对象
let cxt=cnv.getContext('2d')
// 3.绘制直线
cxt.moveTo(50, 50);
cxt.lineTo(100, 50);
cxt.moveTo(50, 100);
cxt.lineTo(100, 100);
cxt.stroke();
//绘制三角形
cxt.moveTo(200, 200);
cxt.lineTo(250, 150);
cxt.lineTo(250, 200);
cxt.lineTo(200, 200);
cxt.stroke();
//绘制矩形
cxt.moveTo(300, 300);
cxt.lineTo(400, 300);
cxt.lineTo(400, 500);
cxt.lineTo(300, 500);
cxt.lineTo(300, 300);
cxt.stroke();
</script>
</body>
</html>
在浏览器中展示如下:
注:
(1)moveTo(x1,y1)的含义是"将画笔移到(x1,y1)位置上,然后开始绘图"。
lineTo(x2,y2)的含义是"将画笔从起点(x1,y1)开始画直线,一直画到终点坐标(x2,y2)"。lineTo()方法是可以重复使用的。第一次使用lineTo()后,Canvas会以"上一个终点坐标"作为第二次调用的起点坐标,然后开始画直线,以此类推。多边形也是使用moveTo()和lineTo()这两个方法画出来的。
(2)moveTo()和lineTo()仅仅是确定直线的“起点坐标”和“终点坐标”这两个状态,但是实际上画笔还没开始动。因此我们还需要调用上下文对象的stroke()方法才有效。
(3)对于绘制矩形,Canvas还为我们提供了独立的方法来实现。在canvas中,矩形分为两种,即"描边"矩形和"填充"矩形。
"描边"矩形语法:
cxt.strokeStyle=属性值
cxt.strokeRect(x,y,width,height)
实现效果等同于:
cxt.strokeStyle=属性值
cxt.rect(x,y,width,height)
cxt.stroke()
"填充"矩形语法:
cxt.fillStyle=属性值
cxt.fillRect(x,y,width,height)
实现效果等同于:
cxt.fillStyle=属性值
cxt.rect(x,y,width,height)
cxt.fill()
曲线图形
圆形
在Canvas中,我们可以使用arc()方法来画一个圆。
语法:
cxt.beginPath();
cxt.arc(x,y,半径,开始角度,结束角度, anticlockwise);
cxt.closePath();
cxt.strokeStyle = "颜色值";
cxt.stroke();
说明:
(1) 我们必须先调用beginPath()方法来声明“开始一个新路径”,然后才可以开始画圆。在使用arc()方法画圆完成之后,还要调用closePath()方法来关闭当前路径。
(2)x和y表示圆心坐标,anticlockwise表示“是否逆时针”。开始角度和结束角度使用的是“弧度”。
(3) 跟矩形一样,对于圆形,我们也可以分为“描边圆形”和“填充圆形”。在Canvas中,我们可以使用stroke()方法来绘制一个“描边圆”,可以使用fill()方法来绘制一个“填充圆”。
弧线
在Canvas中,如果我们想要画弧线,常见的有2种方法:(1)arc();(2)arcTo()。
1、arc()画弧线*
在Canvas中,arc()不仅可以用来画圆形,还可以用来画弧线。
语法:
//状态描述
cxt.beginPath();
cxt.arc(x,y,半径,开始角度,结束角度, anticlockwise);
//描边
cxt.strokeStyle = "颜色值";
cxt.stroke();
说明:
x和y表示圆心坐标,anticlockwise表示“是否逆时针”。当anticlockwise取值为true时,表示按逆时针方向绘制;当anticlockwise取值为false时,表示按顺时针方向绘制。默认情况下,anticlockwise取值为false。 特别注意,“arc()画弧线”与“arc()画描边圆”最大的不同在于:“arc()画弧线不使用closePath()来关闭路径。” 这一点大家一定要区分开,因为弧线不是一个闭合图形。而closePath()是来绘制“封闭图形”的。
2、arcTo()画弧线
在Canvas中,我们可以使用arcTo()方法来画一条弧线。
语法:
cxt.arcTo(cx,cy,x2,y2,radius);
说明:
(cx , cy)表示控制点的坐标,(x2 , y2)表示结束点的坐标,radius表示圆弧的半径。
想要画一条弧线,我们需要提供3个点坐标:开始点、控制点和结束点。其中一般由moveTo()或lineTo()提供开始点,arcTo()提供控制点和结束点。 arcTo()方法就是利用“开始点”、“控制点”和“结束点”这3个点所形成的夹角,然后绘制一段与夹角的两边相切并且半径为radius的圆弧。其中,弧线的起点是“开始点所在边与圆的切点”,而弧线的终点是“结束点所在边与圆的切点”。
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="300" height="300" style="border:1px dashed gray;"></canvas>
<script>
// 1. 找到画布
let cnv=document.getElementById('canvas')
// 2. 获取画笔,上下文对象
let cxt=cnv.getContext('2d')
//半圆
cxt.beginPath();
cxt.arc(80, 80, 50, 0, 180 * Math.PI / 180, true);
cxt.closePath();
//描边
cxt.strokeStyle = "HotPink";
cxt.stroke();
//整圆
cxt.beginPath();
cxt.arc(120, 80, 50, 0, 360 * Math.PI / 180, true);
cxt.closePath();
//描边
cxt.strokeStyle = "HotPink";
cxt.stroke();
//半圆
cxt.beginPath();
cxt.arc(200, 200, 50, 0, 180 * Math.PI / 180, true);
cxt.closePath();
//描边
cxt.fillStyle = "HotPink";
cxt.fill();
//整圆
cxt.beginPath();
cxt.arc(240, 200, 50, 0, 360 * Math.PI / 180, true);
cxt.closePath();
//描边
cxt.fillStyle = "#9966FF";
cxt.fill();
//用arcTo()画弧线cxt.arcTo(cx,cy,x2,y2,radius);
cxt.moveTo(50,150);
cxt.lineTo(100, 150);
cxt.arcTo(150, 150, 150, 190, 50);
cxt.lineTo(150, 220);
cxt.stroke();
</script>
</body>
</html>
运行结果如下:
注:如果想要使用arc()画圆形,需要使用cxt.closePath()来关闭路径;如果想要使用arc()画弧线,则不需要使用cxt.closePath()来关闭路径。closePath()方法的作用在于关闭路径、连接起点与终点。
三. 线条 文本 图片操作
线条操作
这一章,我们主要学习了Canvas的线条操作属性和方法,如下表所示:
线条操作属性
| 属性 | 说明 |
|---|---|
| lineWidth | 定义线条宽度 |
| lineCap | 定义线帽样式 |
| lineJoin | 定义两个线条交接处样式 |
线条操作方法
| 方法 | 说明 |
|---|---|
| setLineDash() | 定义线条的虚实样式 |
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="300" height="400" style="border:1px dashed gray;"></canvas>
<script>
// 1. 找到画布
let cnv=document.getElementById('canvas')
// 2. 获取画笔,上下文对象
let cxt=cnv.getContext('2d')
//lineWidth值为5,lineCap值为默认值(butt)
cxt.lineWidth = 5;
cxt.moveTo(20, 20);
cxt.lineTo(180, 20);
cxt.stroke();
//lineWidth值为10,lineCap值改为round
cxt.beginPath();
cxt.lineWidth = 10;
cxt.lineCap = "round";
cxt.moveTo(20, 70);
cxt.lineTo(180, 70);
cxt.stroke();
//lineWidth值为15,lineCap值改为square
cxt.beginPath();
cxt.lineWidth = 15;
cxt.lineCap = "square";
cxt.moveTo(20, 120);
cxt.lineTo(180, 120);
cxt.stroke();
cxt.moveTo(200, 200);
cxt.lineTo(250, 200);
cxt.lineTo(200, 250);
cxt.lineTo(250, 250);
cxt.lineWidth = 12;
//cxt.lineJoin="miter"为尖角,默认值,cxt.lineJoin="round"为圆角,cxt.lineJoin="bevel"为斜角,
cxt.lineJoin = "miter";
cxt.stroke();
</script>
</body>
</html>
运行结果如下:
文本操作
对于文本操作,Canvas为我们提供了不少方法和属性,具体使用方法请自行查阅api
文本操作“方法”
| 方法* | 说明* |
|---|---|
| fillText() | 绘制“填充”文本 |
| strokeText() | 绘制“描边”文本 |
| measureText() | 用于获取文本的长度 |
文本操作“属性”
| 属性* | 说明* |
|---|---|
| font | 定义文本字体样式(大小、粗细等) |
| textAlign | 定义文本水平对齐方式 |
| textBaseline | 定义文本垂直对齐方式 |
图片操作
在canvas中,我们不仅可以绘制各种形状的图形,还可以将图片导入Canvas中进行各种操作,例如平铺切割等。无论开发的是应用程序还是游戏软件,都离不开图片,因为没有图片就无法让整个界面漂亮起来。在开发canvas游戏的时候,游戏中的地图、背景、人物、物品等都不是用Canvas绘制出来的,大多是用图片来实现的。
这里简单说一下3种图片操作方式:①绘制图片;②平铺图片;③切割图片。
绘制图片
在Canvas中,我们可以使用drawImage()方法来绘制图片。drawImage()方法共有3种调用方式:
(1)drawImage(image , dx , dy)
(2)drawImage(image , dx , dy , dw , dh)
(3)drawImage(image , sx , sy , sw , sh , dx , dy , dw , dh)
说明:
参数image,表示页面中的图片;参数dx,表示图片左上角的横坐标;参数dy,表示图片左上角的纵坐标。参数dw,定义图片的宽度;参数dh,定义图片的高度;参数sx,sy,sw,sh分别表示源图片被截取部分的横坐标,纵坐标,以及宽度和高度。
这3种方法在实际开发中的图片操作中经常用到,它们都有各自的优点和使用场合:
(1)第1种方法仅仅绘制一个图片;
(2)第2种方法可以绘制大小不一样的图片(常用于Canvas游戏开发);
(3)第3种方法可以将部分图像复制到画布。
平铺图片
在Canvas中,我们可以使用createPattern()方法来定义图片的平铺方式。
语法:
var pattern = cxt.createPattern(image , type);
cxt.fillStyle = pattern;
cxt.fillRect();
说明:
想要定义图片的平铺方式,我们需要将createPattern()和fillRect()这两个方法配合使用。
参数image表示被平铺的图片对象,参数type表示图像平铺的方式。参数type有4种取值:no-repeat、repeat-x、repeat-y、repeat。
createPattern()方法type属性取值
| 属性值 | 说明 |
|---|---|
| repeat | 默认值,在水平方向和垂直方向同时平铺 |
| repeat-x | 只在水平方向平铺 |
| repeat-y | 只在垂直方向平铺 |
| no-repeat | 只显示一次(不平铺) |
此外,createPattern()方法不仅可以用于平铺图片,还可以用于平铺其他canvas元素或者平铺video元素(即视频)。
切割图片
在Canvas中,我们可以调用clip()方法切割Canvas中绘制的图片。
语法: cxt.clip();
说明:
想要使用clip()方法切割一张图片,我们需要以下3步:
(1)绘制基本图形;
(2)使用clip()方法;
(3)绘制图片;
代码示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="300" height="300" style="border:1px dashed gray;"></canvas>
<script>
// 1. 找到画布
let cnv=document.getElementById('canvas')
// 2. 获取画笔,上下文对象
let cxt=cnv.getContext('2d')
//第1步,绘制基本图形,用来切割
cxt.beginPath();
cxt.arc(150, 150, 100, 0, 360 * Math.PI / 180, true);
cxt.closePath();
cxt.stroke();
//第2步,使用clip()方法,使得切割区域为上面绘制的基本图形
cxt.clip();
//第3步,绘制一张图片
var image = new Image();
image.src = "test.jpeg";
image.onload = function () {
cxt.drawImage(image, 30, 50);
}
</script>
</body>
</html>
运行结果如下:
四. 变换操作
Canvas为我们提供了以下变形操作的方法。这些变形操作,不仅可以用于图形,还可以用于图像和文字。
Canvas变形操作的方法
| 方法 | 说明 |
|---|---|
| translate() | 平移 |
| scale() | 缩放 |
| rotate() | 旋转 |
| transform()、setTransform() | 变换矩阵 |
| 其中,transform()和setTransform()这2个方法,可自行了解一下。 |
图形平移
在Canvas中,我们可以使用translate()方法来平移图形。
语法: cxt.translate(x,y);
说明: x表示图形在X轴方向移动的距离,默认单位为px。当x为正时,图形向X轴正方向移动;当x为负时,图形向X轴反方向移动。y表示图形在Y轴方向移动的距离,默认单位为px。当y为正时,图形向Y轴正方形移动;当y为负时,图形向Y轴反方向移动。
图形缩放
在Canvas中,我们可以使用scale()方法来对图形进行缩放操作。缩放,指的是“缩小”和“放大”的意思。
语法: cxt.scale(x,y);
说明:
x表示图形在X轴方向的缩放倍数。y表示图形在Y轴方向的缩放倍数。其中,x和y一般情况下都是正数。当x或y取值为0~1之间时,图形进行缩小;当x或y取值大于1时,图形进行放大。
图形旋转
在Canvas中,我们可以使用rotate()方法来旋转图形。
语法:
cxt.rotate(angle);
说明:
参数angle表示图形旋转的角度,取值为-Math.PI2~Math.PI2。注意,rotate()方法的角度是也用弧度来表示的
如果我们想要改变图形的旋转中心,我们可以使用translate()方法来实现。
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="300" height="300" style="border:1px dashed gray;"></canvas>
<script>
// 1. 找到画布
let cnv=document.getElementById('canvas')
// 2. 获取画笔,上下文对象
let cxt=cnv.getContext('2d')
cxt.translate(170, 25);
cxt.fillStyle = "rgba(195,39,43,0.8)";
//cxt.fillRect(x,y,width,height)
cxt.fillRect(0, 0, 100, 50);
let i=1;
let intervalId = setInterval(function() {
cxt.translate(25, 25); //图形平移
cxt.scale(0.95, 0.95); //图形缩放
cxt.rotate(Math.PI / 10); //图形旋转
cxt.fillRect(0, 0, 100, 50);
i++;
if (i > 50) {
clearInterval(intervalId);
}
}, 400);
</script>
</body>
</html>
运行结果如下:
五. 其他
可用canvas实现更多高级的实现,用户可以借助鼠标或键盘参与到canvas动画中去,去实现一些互动的效果,下面是一个简单的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="200" height="200" style="border:1px dashed gray;"></canvas>
<script>
window.onload = function () {
let cnv = document.getElementById('canvas');
let cxt = cnv.getContext("2d");
//初始化一个圆形
drawBall(cnv.width / 2, cnv.height / 2);
//初始化变量
let x = 100;
let y = 75;
//获取按键方向
let key = getKey();
//是否按下键盘
let isMouseDown = false;
//是否游戏结束
isFinish=false;
//随机产生-2~2之间的任意数,作为vx、vy的值
let vx = (Math.random() * 2 - 1) * 2;
let vy = (Math.random() * 2 - 1) * 2;
//添加键盘松开事件
window.addEventListener("keyup", function (e) {
//清除整个Canvas,以便重绘新的圆形
isMouseDown=false;
console.log('进来了');
automate()
}, false);
window.addEventListener("keydown", function (e) {
//清除整个Canvas,以便重绘新的圆形
isMouseDown=true;
cxt.clearRect(0, 0, cnv.width, cnv.height);
//根据key.direction的值,判断小球移动方向
switch (key.direction) {
case "up":
y -= 2;
drawBall(x, y);
checkBorder();
break;
case "down":
y += 2;
drawBall(x, y);
checkBorder();
break;
case "left":
x -= 2;
drawBall(x, y);
checkBorder();
break;
case "right":
x += 2;
drawBall(x, y);
checkBorder();
break;
//default值
default:
drawBall(x, y);
}
}, false);
//定义绘制小球的函数
function drawBall(x, y) {
cxt.beginPath();
//cxt.arc(x,y,半径,开始角度,结束角度, anticlockwise);
cxt.arc(x, y, 10, 0, 360 * Math.PI / 180, true);
cxt.closePath();
cxt.fillStyle = "#6699FF";
cxt.fill();
}
//获取键盘控制方向
function getKey () {
let key = {};
window.addEventListener("keydown", function (e) {
if (e.keyCode == 38 || e.keyCode == 87) {
key.direction = "up";
} else if (e.keyCode == 39 || e.keyCode == 68) {
key.direction = "right";
} else if (e.keyCode == 40 || e.keyCode == 83) {
key.direction = "down";
} else if (e.keyCode == 37 || e.keyCode == 65) {
key.direction = "left";
} else {
key.direction = "";
}
}, false);
return key;
}
//定义边界检测函数
function checkBorder() {
//当小球碰到上边界时
if (y <= 10) {
isFinish=true;
alert('Game Over');
//当小球碰到下边界时
} else if (y >= cnv.height - 10) {
isFinish=true;
alert('Game Over');
}
//当小球碰到左边界时
if (x <= 10) {
isFinish=true;
alert('Game Over');
} else if (x >= cnv.width - 10) {
isFinish=true;
alert('Game Over');
}
}
function automate() {
let intervalId = setInterval(function() {
console.log(isMouseDown)
if (!isMouseDown &&!isFinish) {
x += vx;
y += vy;
cxt.clearRect(0, 0, cnv.width, cnv.height);
drawBall(x, y);
checkBorder()
}else{
clearInterval(intervalId);
}
}, 100);
}
automate()
}
</script>
</body>
</html>
通过以上代码可简单的控制一个小球的移动。