<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
html {
background: #333;
}
</style>
</head>
<body>
<!--
canvas动画
特点:
1. 把之前的画布清空
2. 更新位置,重新画一遍
-->
<canvas></canvas>
<script>
// 获取画布元素
const cvs = document.querySelector('canvas');
// 获取canvas绘制上下文
const ctx = cvs.getContext('2d');
// 初始化画布
function init() {
// 更正分辨率
cvs.width = window.innerWidth * devicePixelRatio;
cvs.height = window.innerHeight * devicePixelRatio;
}
init();
/**
* 获取min到max之间的随机数
* @param {Number} min
* @param {Number} max
* @return {Number}
* **/
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
// 创建一个画点的类
class Point {
constructor() {
this.r = 6;
this.x = getRandom(0, cvs.width - this.r / 2);
this.y = getRandom(0, cvs.height - this.r / 2);
// 给小点添加速度
this.xSpeed = getRandom(-500, 500);
this.ySpeed = getRandom(-500, 500);
// 记录上一次画的时间,通过时间和速度计算出移动的距离
this.lastDrawTime = null;
}
// 画点
draw() {
if (this.lastDrawTime) {
// 计算新的坐标
const duration = Date.now() - this.lastDrawTime;
let x = this.x + (this.xSpeed * duration) / 1000;
let y = this.y + (this.ySpeed * duration) / 1000;
// 考虑边界条件
if (x > cvs.width - this.r / 2) {
this.x = cvs.width - this.r / 2;
this.xSpeed = -this.xSpeed;
} else if (x < 0) {
this.x = 0;
this.xSpeed = -this.xSpeed;
}
if (y > cvs.height - this.r / 2) {
this.y = cvs.height - this.r / 2;
this.ySpeed = -this.ySpeed;
} else if (y < 0) {
this.y = 0;
this.ySpeed = -this.ySpeed;
}
this.x = x;
this.y = y;
}
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgb(200,200,200)';
ctx.fill();
this.lastDrawTime = Date.now();
}
}
// 创建画图的类
class Graph {
// 点与点之间随着距离的增大,连线的透明度逐渐降低,超过最大距离则透明度为0
constructor(pointNum = 30, maxDis = 500) {
this.points = new Array(pointNum).fill(0).map(() => new Point());
this.maxDis = maxDis;
}
draw() {
// 递归调用,不断的清空画布,重新画
requestAnimationFrame(() => {
this.draw();
});
// 每次画之前先清空画布
ctx.clearRect(0, 0, cvs.width, cvs.height);
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
p1.draw();
// 与别的点进行连线
for (let j = i + 1; j < this.points.length; j++) {
const p2 = this.points[j];
const d = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
if (d > this.maxDis) {
continue;
}
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.strokeStyle = `rgba(200,200,200,${1 - d / this.maxDis})`;
ctx.stroke();
}
}
}
}
const g = new Graph();
g.draw();
</script>
</body>
</html>
requestAnimationFrame特点
- 性能高效:他能根据浏览器的刷新频率自动调整动画的帧率,使得动画可以在不同设备上都能以最佳性能运行
- 平滑动画:会在浏览器下一次重绘之前执行动画
- 页面失去焦点时,停止动画运行,获取焦点后继续执行动画