canvas入门,跟随掘金小册如何使用 Canvas 制作出炫酷的网页背景特效学习,完成一个简单的 canvas demo
下面是代码,操作解释都有注释:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Canvas Demo</title>
<style>
*{
margin: 0;
}
html,body {
margin: 0;
overflow: hidden;
width: 100%;
height: 100%;
cursor: none;
background: #000;
}
</style>
</head>
<body>
<div id="app"></div>
<canvas id="canvas"></canvas>
</body>
<script>
const ctx = document.getElementById("canvas"),
context = ctx.getContext("2d"), //获取 canvas 上下文
docWidth = document.documentElement.clientWidth,
docHeight = document.documentElement.clientHeight,
config = {
num: 100,
r: 1, //圆每次增加的半径
option: 0.05 // 判断圆消失的条件,数值越大,消失的越快
},
useCache = true; //离屏渲染
ctx.width = docWidth;
ctx.height = docHeight;
//随机小球
class Round_Item{
constructor (index, x, y) {
this.index = index;
this.x = x;
this.y = y;
this.r = Math.random() * 2 + 1;
var alpha = (Math.floor(Math.random() * 10) + 1) / 10 / 2; //不透明度
this.color = "rgba(255,255,255," + alpha + ")";
if (useCache) {
//创建一个不渲染的离线 canvas 小球
this.cacheCanvas = document.createElement('canvas');
this.cacheContext = this.cacheCanvas.getContext('2d');
//考虑到阴影,设置宽高为 6 * 半径
this.cacheContext.width = 6 * this.r;
this.cacheContext.height = 6 * this.r;
this.cache();
}
}
cache () {
this.cacheContext.save(); //保存canvas状态
this.cacheContext.fillStyle = this.color; //填充颜色
this.cacheContext.shadowColor = "white"; //阴影颜色
this.cacheContext.shadowBlur = this.r * 2; //阴影距离
this.cacheContext.beginPath(); //开始路径绘制
this.cacheContext.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI); //绘制圆形路径
this.cacheContext.closePath(); //闭合路径
this.cacheContext.fill(); //填充
this.cacheContext.restore(); //恢复上一次保存的状态,即默认状态,此时上面小球已经绘制完成
}
draw () {
if (!useCache) {
context.fillStyle = this.color;
context.shadowBlur = this.r * 2;
context.shadowColor = '#fff';
context.beginPath();
context.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
context.closePath();
context.fill();
} else {
context.drawImage(this.cacheCanvas, this.x - this.r, this.y - this.r); //将离线 canvas 绘制到页面中
}
}
move () {
// 移动条件
this.y -= 0.3;
if (this.y <= -10) {
this.y = docHeight + 10; //离开屏幕则重新赋 Y 值
}
this.draw();
}
}
//鼠标滑动小球
class Mouse_Item{
constructor (mouseX, mouseY, radius, option, color) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.radius = radius;
this.option = option;
this.color = color;
}
draw () {
context.fillStyle = 'hsl(' + this.color + ',100%,80%)'; //hsl(h,s,l); h 色调 s 饱和度 l 亮度
context.shadowBlur = this.radius * 2;
context.shadowColor = '#fff';
context.beginPath();
context.arc(this.mouseX, this.mouseY, this.radius, 0, 2 * Math.PI, false);
context.closePath();
context.fill();
}
move () {
this.radius += config.r; //逐渐增大半径
this.option -= config.option; //消失条件
this.color += .1;
if( this.option <= 0){
return true;
}
this.draw();
}
}
class InitCanvas{
constructor (count) {
this.initRoundPopulation = count;
this.populations = [];
this.mousePopu = [];
}
init () {
for (let i = 0; i < this.initRoundPopulation; i++) {
this.populations.push(new Round_Item(i, Math.random() * docWidth,Math.random() * docHeight));
this.populations[i].draw();
}
this.animate();
}
animate() {
context.clearRect(0, 0, docWidth, docHeight); //清空 canvas 以便后面重新绘制
for (let item of this.populations) {
item.move();
}
if (this.mousePopu.length) {
for (let i = 0; i< this.mousePopu.length; i++) {
if (this.mousePopu[i].move()) { //鼠标渲染小球,满足消失条件则去除
this.mousePopu.splice(i,1);
i--;
}
}
}
requestAnimationFrame(_ => this.animate()); // js 动画渲染函数
}
}
let initCanvas = new InitCanvas(config.num);
initCanvas.init();
// 监听 mousemove 事件,添加鼠标小球
window.onmousemove = function (event) {
mouseX = event.clientX;
mouseY = event.clientY;
initCanvas.mousePopu.push(new Mouse_Item(mouseX, mouseY, config.r, 1, Math.random() * 360));
};
</script>
</html>