历史
Canvas由 Apple 在 Safari 1.3 Web 浏览器中引入。对 HTML 的这一根本扩展的原因在于,HTML 在 Safari 中的绘图能力也为 Mac OS X 桌面Dashboard 组件所使用,并且 Apple 希望有一种方式在 Dashboard 中支持脚本化的图形。Firefox 1.5 和 Opera 9 都跟随了 Safari 的引领。这两个浏览器都支canvas。我们甚至可以在 IE 中使用 。Canvas 的标准化的努力由一个 Web 浏览器厂商的非正式协会在推进,已经成为 HTML 5 草案中一个正式的标签。
概述
Canvas是一个矩形区域的画布,使用 js在网页上绘制图像,本身不具备绘图功能。Canvas API拥有多种绘制路径、矩形、圆形、字符以及添加图像等方法。
应用场景
1、可视化数据: 各类统计图表,如百度的echart等。
2、 场景秀: 用canvas实现动态的广告效果能够非常融洽的跨平台运行。
3、 游戏: canvas在基于Web的图像显示方面比Flash更加立体、更加精巧,成为H5小游戏开发首选。
4、图形编辑器: 一些图形编辑器将能够100%基于Web实现,如PS等。
使用
创建画布
绘画第一步,先拿张纸。通过html和js两张方式。
html创建
<!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>Canvas</title>
</head>
<body>
<canvas width="200" height="200">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
</body>
</html>
js创建
const canvas = document.createdElement('canvas')
canvas.width = 200
canvas.height = 200
document.body.appendChild(canvas)
创建渲染上下文
画布就相当于一张纸,光有纸,没有其他绘画工具是不行的,所以需要创建渲染上下文来获取这些工具。通过getContext() 方法。
<!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>Canvas</title>
</head>
<body>
<canvas id="canvas" width="200" height="200">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
// 获取 canvas 元素
const canvas = document.querySelector('#canvas');
// 获取渲染上下文
const ctx = canvas.getContext('2d');
</script>
</body>
</html>
开始绘画
绘制图形
canvas为我们提供了一些现成的几何图形和线条等。
上面我们已经准备好了纸和工具,在开始绘画前还是要先了解一下工具的一些基本用法。
moveTo(x, y)
设置初始位置,相当于下笔点。
lineTo(x, y)
绘制一条从初始位置到指定位置的直线,相当于收笔点。
stroke()
通过线条来绘制图形轮廓,上面两步是规划,这是真正执行绘画动作。
fill()
通过填充路径的内容区域,上面两步是规划,这是真正执行绘画动作。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="500" height="500">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// 绘制一条从起点50,50到另一个点200,200的直线
ctx.moveTo(50, 50);
ctx.lineTo(200, 200);
ctx.stroke();
// 画个线框矩形
// strokeRect(起点x, 起点y, 宽, 高)
ctx.strokeRect(50, 50, 200, 200)
// 画个填充矩形
// fillRect(起点x, 起点y, 宽, 高)
ctx.fillRect(300, 300, 100, 100)
// 矩形擦除(矩形橡皮擦)
// clearRect(起点x, 起点y, 宽, 高)
ctx.clearRect(300, 300, 50, 50)
// 角度转弧度的表达式:弧度 = (Math.PI / 180) * 角度
// 画个线框圆弧
// arc(圆心x, 圆心y, 半径, 开始弧度, 结束弧度, 绘制方向-默认false,顺时针)
ctx.beginPath()
ctx.arc(250, 250, 100, 0 , 2 * Math.PI, false)
ctx.stroke()
ctx.closePath()
// 画个填充圆弧
ctx.beginPath()
ctx.arc(250, 400, 50, 0 , 2 * Math.PI, false)
ctx.fill()
ctx.closePath()
// 画个线框椭圆
// ellipse(圆心x, 圆心y, x轴半径, y轴半径, 旋转弧度, 开始弧度, 结束弧度, 绘制方向-默认false,顺时针)
ctx.beginPath()
ctx.ellipse(250, 250, 50, 100, Math.PI / 2, 0, 2 * Math.PI)
ctx.stroke()
// 画个填充椭圆
ctx.beginPath()
ctx.ellipse(250, 250, 50, 100, 1, 0, 2 * Math.PI)
ctx.fill()
// 二次贝塞尔曲线
// quadraticCurveTo(控制点x, 控制点y, 结束点x, 结束点y)
ctx.beginPath()
ctx.moveTo(50, 50)
ctx.quadraticCurveTo(200, 200, 350, 50)
ctx.stroke()
// 三次贝塞尔曲线
// bezierCurveTo(控制点x1, 控制点y1, 控制点x2, 控制点y2, 结束点x, 结束点y)
ctx.beginPath()
ctx.moveTo(50, 200)
ctx.bezierCurveTo(150, 50, 250, 350, 350, 200)
ctx.stroke()
</script>
</body>
</html>
绘制样式
好看的画面肯定少不了好看颜色、漂亮的图案和不同样式的线条等。canvas提供了一些属性来实现这些。
线条样式
lineWidth 设置当前绘线的粗细,值必须为正数,默认1。
lineCap 设置线段两端样式,可选butt,round 和 square,默认butt。
lineJoin 设置两线段连接处所显示的样子,可选round,bevel和miter,默认miter。
miterLimit 限制当两段线相交时交接处内角顶点到外角顶点的长度。如果交点距离大于miterLimit值,连接处会变成了lineJoin为bevel的效果。
setLineDash 设置线条为虚线,如果传参为奇数,会复制一份数组补全为偶数。
getLineDash 返回线条虚线设置的样式,长度为非负偶数的数组。
lineDashOffset 设置虚线样式的起始偏移量。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
var canvas = document.getElementById('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
// 绘制一条宽度为10的直线
ctx.beginPath()
ctx.lineWidth = 10;
ctx.moveTo(50, 20);
ctx.lineTo(250, 20);
ctx.stroke();
ctx.closePath();
// 绘制一条宽度为20的直线
ctx.beginPath()
ctx.lineWidth = 20;
ctx.moveTo(50, 50);
ctx.lineTo(250, 50);
ctx.stroke();
ctx.closePath();
// lineWidth 设置当前绘线的粗细,值必须为正数,默认1。
// 绘制一条宽度为10的直线
ctx.beginPath()
ctx.lineWidth = 10;
ctx.moveTo(50, 20);
ctx.lineTo(250, 20);
ctx.stroke();
// lineCap 设置线段两端样式,可选butt,round 和 square,默认butt。
// butt
ctx.beginPath()
ctx.lineCap='butt'
ctx.moveTo(50, 20);
ctx.lineTo(250, 20);
ctx.stroke();
// round
ctx.beginPath()
ctx.lineCap='round'
ctx.moveTo(50, 50);
ctx.lineTo(250, 50);
ctx.stroke();
// square
ctx.beginPath()
ctx.lineCap='square'
ctx.moveTo(50, 80);
ctx.lineTo(250, 80);
ctx.stroke();
// lineJoin 设置两线段连接处所显示的样子,可选round,bevel和miter,默认miter。
// miter
ctx.beginPath()
ctx.lineJoin='miter'
ctx.moveTo(50, 20);
ctx.lineTo(100, 60);
ctx.lineTo(150, 20);
ctx.lineTo(200, 60);
ctx.lineTo(250, 20);
ctx.stroke();
// round
ctx.beginPath()
ctx.lineJoin='round'
ctx.moveTo(50, 100);
ctx.lineTo(100, 140);
ctx.lineTo(150, 100);
ctx.lineTo(200, 140);
ctx.lineTo(250, 100);
ctx.stroke();
// bevel
ctx.beginPath()
ctx.lineJoin='bevel'
ctx.moveTo(50, 180);
ctx.lineTo(100, 220);
ctx.lineTo(150, 180);
ctx.lineTo(200, 220);
ctx.lineTo(250, 180);
ctx.stroke();
// miterLimit 限制当两段线相交时交接处内角顶点到外角顶点的长度。
// 如果交点距离大于miterLimit值,连接处会变成了lineJoin为bevel的效果。
ctx.beginPath()
ctx.lineWidth = 5;
ctx.lineJoin='miter'
ctx.miterLimit = 10
ctx.moveTo(0, 100);
for (i = 0; i < 30 ; i++) {
const dy = i % 2 == 0 ? 200 : 100;
ctx.lineTo(Math.pow(i, 1.5) * 2, dy);
}
ctx.stroke();
ctx.closePath();
// setLineDash 设置线条为虚线,如果传参为奇数,会复制一份数组补全为偶数。
// getLineDash 返回线条虚线设置的样式,长度为非负偶数的数组。
// 绘制一条虚线
ctx.setLineDash([5, 10, 20]);
console.log(ctx.getLineDash()); // [5, 10, 20, 5, 10, 20]
ctx.beginPath();
ctx.moveTo(0,100);
ctx.lineTo(400, 100);
ctx.stroke();
// 再绘制一条虚线
ctx.setLineDash([5, 10, 20, 40]);
console.log(ctx.getLineDash()); // [5, 10, 20, 40]
ctx.beginPath();
ctx.moveTo(0,200);
ctx.lineTo(400, 200);
ctx.stroke();
// lineDashOffset 设置虚线样式的起始偏移量。
// 起始点向左位移了3像素
ctx.lineDashOffset = 3;
}
</script>
</body>
</html>
透明度
globalAlpha 设置透明度值,取值0~1。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// 绘制一个圆
ctx.beginPath()
ctx.fillStyle = "rgba(255, 255, 0, 1)";
// globalAlpha 设置透明度值,0 ~ 1。
ctx.globalAlpha = 0.5;
ctx.arc(200, 200, 100, 0, Math.PI*2, true);
ctx.fill();
</script>
</body>
</html>
渐变
createLinearGradient(起点x, 起点y, 终点x, 终点y) 设置线性渐变。
createRadialGradient(圆心x0, 圆心y0, 半径r0, 圆心x1, 圆心y1, 半径r1) 设置径向渐变。
addColorStop(offset, color) 添加渐变的颜色,offset颜色偏移值,取值0 ~1,color颜色。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// 创建线性渐变
const gradient1 = ctx.createLinearGradient(10, 10, 400, 10);
gradient1.addColorStop(0, "#00ff00");
gradient1.addColorStop(1, "#ff0000");
const gradient2 = ctx.createLinearGradient(10, 10, 400, 10);
// 从0.5的位置才开始渐变
gradient2.addColorStop(0.5, "#00ff00");
gradient2.addColorStop(1, "#ff0000");
ctx.beginPath()
ctx.fillStyle = gradient1;
ctx.fillRect(10, 10, 400, 100);
ctx.beginPath();
ctx.fillStyle = gradient2;
ctx.fillRect(10, 150, 400, 100);
</script>
</body>
</html>
图案样式
createPattern(image, type) 绘制图案效果,image 图片对象,type 绘制方式,可选repeat,repeat-x,repeat-y 和 no-repeat。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// 创建Image对象
const img = new Image();
img.src = "./image.png";
img.onload = () => {
// 创建图案
// const ptrn = ctx.createPattern(img, 'repeat');
// const ptrn = ctx.createPattern(img, 'repeat-x');
// const ptrn = ctx.createPattern(img, 'repeat-y');
const ptrn = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 500, 500);
}
</script>
</body>
</html>
绘制文本
canvas提供了描边和填充两种方法来渲染文本。
strokeText(文本, 起点x, 起点y[, 最大宽度]) 描边创建文本。
fillText(文本, 起点x, 起点y[, 最大宽度]) 填充填充文本。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// 设置文本大小和字体
ctx.font = "50px serif";
ctx.strokeText("Canvas", 50, 50);
ctx.fillText("Canvas", 50, 250);
</script>
</body>
</html>
绘制图片
drawImage(image[, sx, sy, swidth, sheight], dx, dy[, width, height]) 绘制图片、视频和别的Canvas对象等。image为一个Image对象,sx、sy为图片裁切位置,swidth,sheight为图片裁切的宽高,dx、dy为图片位置,width,height为绘制图片的宽高。
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
const image = new Image()
image.src = '/assets/images/dog.png'
image.onload = () => {
// 将图片以自身尺寸绘制在画布0,0(左上角)的位置
ctx.drawImage(image, 0, 0)
// 将图片以100 x 100的尺寸绘制在画布0,0(左上角)的位置
ctx.drawImage(image, 0, 0, 100, 100)
// 将图片从图上10,10的位置裁切一个宽高为100 x 100的部分,再以200 x 200的尺寸绘制在画布20,20(左上角)的位置
ctx.drawImage(image, 10, 10, 100, 100, 20, 20, 200, 200)
}
</script>
</body>
</html>
变形
translate(x, y) 移动,x 是左右偏移量,y 是上下偏移量。
rotate(angle) 旋转,angle是旋转的角度,它是顺时针旋转,以弧度为单位的值。
scale(x, y) 缩放,x 为水平缩放的值,y 为垂直缩放得值。x和y的值小于1则为缩小,大于1则为放大。默认值为 1。
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas" width="300" height="300">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ee7034';
// 移动原点到100,100
ctx.translate(100, 100);
// 画一个100,100的矩形,由于上面移动了原点,所以其实是从画布100,100开始绘制的
ctx.fillRect(0, 0, 100, 100);
// 旋转了45度,Math.PI=180度
ctx.rotate(Math.PI / 4);
ctx.fillRect(0, 0, 100, 100);
// 缩放
ctx.scale(2, 2);
ctx.fillRect(100, 100, 100, 100);
</script>
</body>
</html>