Canvas API 提供了一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
1、基础结构
canvas元素本身没有任何外观,它就是一块空白画板,提供给JS的一套API,最早由Safari引入,IE9之前可以使用一些类库在IE中模拟canvas,大部分的API都不在canvas元素自身定义,canvas元素自身属性与常规的HTML元素并没有多大区别, 它的绘图API都定义在一个CanvasRenderingContext2D
对象上(这里简单翻译成上下文对象),该对象可以通过getContext()
方法获得,代码示例:
<html>
<head>
<title>demo</title>
</head>
<body>
<canvas id = 'canvasId' width= 200 heigth=200></canvas>
</body>
<script>
var canvas = document.getElementById('canvasId')
var ctx = canvas.getContext('2d')//2d表示画板维度,输入3d将得到一个更为复杂的3d图形API,也称WebGL
</script>
2、基本用法
绘制矩形
- fillRect(x, y, width, heigh) => 绘制一个填充的矩形
- strokeRect(x, y, width, heigh) => 绘制一个矩形的边框
- clearRect(x, y, width, heigh) => 清除指定矩形区域,让清除部分完全透明
代码使用示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<canvas id="myCanvas" width="200" height="200" style="border:1px solid #c3c3c3;">
</canvas>
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillRect(25, 25, 150, 150);
ctx.clearRect(45, 45, 30, 30);
ctx.strokeRect(50, 50, 20, 20);
// ctx.clearRect(125, 45, 30, 30);
// ctx.strokeRect(130, 50, 20, 20);
</script>
</body>
</html>
运行结果如图:
绘制形状(重点)
- beginPath() => 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
- closePath() => 闭合路径之后图形绘制命令又重新指向到上下文中。
- stroke() => 通过线条来绘制图形轮廓。
- fill() => 通过填充路径的内容区域生成实心的图形。
- moveTo(x,y) => 定义线条开始坐标。
- arc(x, y, r, start, stop) => 绘制一个以 r 为半径的圆形。
- lineTo(x, y) => 绘制一条从当前位置到指定x以及y位置的直线。
使用示例如下(笑脸):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<canvas id="myCanvas" width="200" height="200" style="border:1px solid #c3c3c3;">
</canvas>
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // 绘制
ctx.moveTo(110, 75);
ctx.arc(75, 75, 35, 0, Math.PI, false); // 口(顺时针)
ctx.moveTo(65, 65);
ctx.arc(60, 65, 5, 0, Math.PI * 2, true); // 左眼
ctx.moveTo(95, 65);
ctx.arc(90, 65, 5, 0, Math.PI * 2, true); // 右眼
ctx.stroke();
</script>
</body>
</html>
运行结果如下:
注意:
1、线条(lineTo)绘制的用法也是差不多,只需要在适当的位置(x, y), lineTo(x, y) 就可以得到你想要的线性图形(比如三角形等),这里就不另外示例;
2、闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做;
3、当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。
3、基本动画
首先,可以用 window.setInterval(), window.setTimeout() 和window.requestAnimationFrame() 来设定定期执行一个指定函数。
setInterval(function, delay) (en-US) => 当设定好间隔时间后,function会定期执行。
setTimeout(function, delay) (en-US) => 在设定好的时间之后执行函数。
requestAnimationFrame(callback) =>
告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。
下面是示例代码:
var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
window.requestAnimationFrame(draw);
}
function draw() {
var ctx = document.getElementById('myCanvas').getContext('2d');
// 设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。
ctx.globalCompositeOperation = 'destination-over';
ctx.clearRect(0,0,300,300); // clear canvas
ctx.fillStyle = 'rgba(0,0,0,0.4)';
ctx.strokeStyle = 'rgba(0,153,255,0.4)'; // 设置或返回用于笔触的颜色、渐变或模式。
ctx.save(); // 如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
ctx.translate(150,150); // 垂直和水平方向的偏移
// 地球
var time = new Date();
ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
ctx.translate(105,0);
ctx.fillRect(0,-12,50,24); // 阴影
ctx.drawImage(earth,-12,-12);
// 月球
ctx.save();
ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
ctx.translate(0,28.5);
ctx.drawImage(moon,-3.5,-3.5);
ctx.restore(); // 用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
ctx.restore();
ctx.beginPath();
ctx.arc(150,150,105,0,Math.PI*2,false); // 地球轨道
ctx.stroke();
ctx.drawImage(sun,0,0,300,300); // 太阳
window.requestAnimationFrame(draw);
}
init();
以下为运行结果:
上面的例子,采用
window.requestAnimationFrame()实现动画效果。这个方法提供了更加平缓并更加有效率的方式来执行动画,当系统准备好了重绘条件的时候,才调用绘制动画帧。一般每秒钟回调函数执行60次,也有可能会被降低。
4、高级动画
高级动画其实就是在基础动画的基础上添加一些符合物理的运动,比如速率、拖尾、鼠标控制等等,进而让动画变得更高级。
下面是示例代码:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var raf;
var ball = {
x: 100,
y: 100,
vx: 5,
vy: 2,
radius: 25,
color: 'blue',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
function draw() {
ctx.clearRect(0,0, canvas.width, canvas.height);
ball.draw();
ball.x += ball.vx;
ball.y += ball.vy;
raf = window.requestAnimationFrame(draw);
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
ball.vx = -ball.vx;
}
}
canvas.addEventListener('mouseover', function(e){
raf = window.requestAnimationFrame(draw);
});
canvas.addEventListener('mouseout', function(e){
window.cancelAnimationFrame(raf);
});
ball.draw();
运行结果如下:
上述案例依然是使用了 window.requestAnimationFrame() 来控制动画,然后小球按照一定的速率进行移动,然后设置边界值,超越边界的时候将速率反转,产生反弹的效果。同时,还给canvas添加了鼠标移入移出的事件监听,只有鼠标移入的时候才进行动画请求,鼠标移出的时候就取消请求(window.cancelAnimationFrame()
)。