Canvas背景动画(一) 粒子矩阵特效
- 效果演示:
- 代码附上
<!DOCTYPE html>
<html>
<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>
<style>
canvas {
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="canvasBox"></canvas>
<script type="text/javascript">
var c = document.getElementById('canvasBox')
var w = (c.width = window.innerWidth),
h = (c.height = window.innerHeight),
ctx = c.getContext("2d"),
opts = {
len: 50, // 长度 定义基础模块大小
count: 50, // 粒子数量
baseTime: 10, // 粒子基础运动时间
addedTime: 10, // 新增粒子间隔
dieChance: 0.05, // 粒子随机消散
spawnChance: 1, // 火花比例
sparkChance: 0.1, // 火花产随机数
sparkDist: 10, // 火花区域
sparkSize: 2 ,// 火花大小
color: "hsl(hue,100%,light%)", // 颜色系数(色相、饱和度、亮度)
baseLight: 50, // 光源开始数值
addedLight: 10, //新增光源间隔
shadowToTimePropMult: 6, // 阴影显示时间比例
baseLightInputMultiplier: 0.01, // 光源初始系数
addedLightInputMultiplier: 0.02,// 新增光源初始系数
cx: w / 2,
cy: h / 2,
repaintAlpha: 0.04, // 重绘粒子系数
hueChange: 0.1, // 颜色色相变化系数
},
tick = 0, // 初始色相值
lines = [], // 线路数组
dieX = w / 2 / opts.len,
dieY = h / 2 / opts.len,
baseRad = (Math.PI * 2) / 6;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w, h);
function loop() {
window.requestAnimationFrame(loop);
++tick;
ctx.globalCompositeOperation = "source-over";
ctx.shadowBlur = 0;
ctx.fillStyle = "rgba(0,0,0,alp)".replace(
"alp",
opts.repaintAlpha
);
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "lighter";
if (
lines.length < opts.count &&
Math.random() < opts.spawnChance
)
lines.push(new Line());
lines.map(function (line) {
line.step();
});
}
function Line() {
this.reset();
}
Line.prototype.reset = function () {
this.x = 0;
this.y = 0;
this.addedX = 0;
this.addedY = 0;
this.rad = 0;
this.lightInputMultiplier =
opts.baseLightInputMultiplier +
opts.addedLightInputMultiplier * Math.random();
this.color = opts.color.replace("hue", tick * opts.hueChange);
this.cumulativeTime = 0;
this.beginPhase();
};
Line.prototype.beginPhase = function () {
this.x += this.addedX;
this.y += this.addedY;
this.time = 0;
this.targetTime =
(opts.baseTime + opts.addedTime * Math.random()) | 0;
this.rad += baseRad * (Math.random() < 0.5 ? 1 : -1);
this.addedX = Math.cos(this.rad);
this.addedY = Math.sin(this.rad);
if (
Math.random() < opts.dieChance ||
this.x > dieX ||
this.x < -dieX ||
this.y > dieY ||
this.y < -dieY
)
this.reset();
};
Line.prototype.step = function () {
++this.time;
++this.cumulativeTime;
if (this.time >= this.targetTime) this.beginPhase();
var prop = this.time / this.targetTime,
wave = Math.sin((prop * Math.PI) / 2),
x = this.addedX * wave,
y = this.addedY * wave;
ctx.shadowBlur = prop * opts.shadowToTimePropMult;
ctx.fillStyle = ctx.shadowColor = this.color.replace(
"light",
opts.baseLight +
opts.addedLight *
Math.sin(
this.cumulativeTime * this.lightInputMultiplier
)
);
ctx.fillRect(
opts.cx + (this.x + x) * opts.len,
opts.cy + (this.y + y) * opts.len,
2,
2
);
if (Math.random() < opts.sparkChance)
ctx.fillRect(
opts.cx +
(this.x + x) * opts.len +
Math.random() *
opts.sparkDist *
(Math.random() < 0.5 ? 1 : -1) -
opts.sparkSize / 2,
opts.cy +
(this.y + y) * opts.len +
Math.random() *
opts.sparkDist *
(Math.random() < 0.5 ? 1 : -1) -
opts.sparkSize / 2,
opts.sparkSize,
opts.sparkSize
);
};
loop();
window.addEventListener("resize", function () {
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w, h);
opts.cx = w / 2;
opts.cy = h / 2;
dieX = w / 2 / opts.len;
dieY = h / 2 / opts.len;
});
</script>
</body>
</html>