Vanta 风格的背景,粒子效果
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vanta Style Background</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
font-family: Arial, sans-serif;
}
#vanta-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1; /* 确保背景在内容下方 */
}
.content {
position: relative;
z-index: 1;
padding: 2rem;
color: white;
text-align: center;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
p {
font-size: 1.2rem;
max-width: 600px;
text-shadow: 0 1px 5px rgba(0,0,0,0.3);
}
</style>
</head>
<body>
<!-- Vanta 风格的背景容器 -->
<div id="vanta-background">
<canvas id="background-canvas"></canvas>
</div>
<!-- 页面内容 -->
<div class="content">
<h1>Dynamic Background Demo</h1>
<p>This is an example of a Vanta-style dynamic background effect using HTML5 Canvas.</p>
</div>
<script>
// 获取 canvas 元素和上下文
const canvas = document.getElementById('background-canvas');
const ctx = canvas.getContext('2d');
// 设置 canvas 尺寸以匹配窗口
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
// 初始化时调整尺寸,并监听窗口大小变化
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 粒子类
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.size = Math.random() * 3 + 1; // 粒子大小 1-4px
this.speedX = Math.random() * 0.5 - 0.25; // X方向速度
this.speedY = Math.random() * 0.5 - 0.25; // Y方向速度
this.color = 'rgba(255, 255, 255, 0.8)'; // 粒子颜色
}
// 更新粒子位置
update() {
this.x += this.speedX;
this.y += this.speedY;
// 边界检查 - 超出边界后从另一边出现
if (this.x > canvas.width) this.x = 0;
else if (this.x < 0) this.x = canvas.width;
if (this.y > canvas.height) this.y = 0;
else if (this.y < 0) this.y = canvas.height;
}
// 绘制粒子
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
// 线条连接类
class Line {
constructor(particles, maxDistance) {
this.particles = particles;
this.maxDistance = maxDistance;
this.lineColor = 'rgba(255, 255, 255, '; // 线条基础颜色
}
// 绘制粒子间的连接线
draw() {
for (let i = 0; i < this.particles.length; i++) {
for (let j = i + 1; j < this.particles.length; j++) {
const p1 = this.particles[i];
const p2 = this.particles[j];
// 计算粒子间距离
const dx = p1.x - p2.x;
const dy = p1.y - p2.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 如果距离小于最大距离,则绘制连接线
if (distance < this.maxDistance) {
// 距离越远,线条越淡
const opacity = 1 - (distance / this.maxDistance);
ctx.strokeStyle = this.lineColor + opacity + ')';
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
}
}
}
}
// 创建粒子数组
const particlesArray = [];
const particleCount = 80; // 粒子数量
// 初始化粒子
function init() {
for (let i = 0; i < particleCount; i++) {
particlesArray.push(new Particle());
}
}
// 创建线条连接对象
const lines = new Line(particlesArray, 150); // 最大连接距离 150px
// 动画循环
function animate() {
// 绘制半透明黑色背景以创建轨迹效果
ctx.fillStyle = 'rgba(10, 10, 30, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 更新和绘制所有粒子
for (let i = 0; i < particlesArray.length; i++) {
particlesArray[i].update();
particlesArray[i].draw();
}
// 绘制连接线
lines.draw();
// 递归调用动画
requestAnimationFrame(animate);
}
// 鼠标交互效果
let mouse = {
x: null,
y: null,
radius: 150 // 鼠标影响范围
};
// 监听鼠标移动
window.addEventListener('mousemove', function(event) {
mouse.x = event.x;
mouse.y = event.y;
});
// 鼠标离开窗口时重置鼠标位置
window.addEventListener('mouseout', function() {
mouse.x = null;
mouse.y = null;
});
// 增强粒子与鼠标的交互
function interactWithMouse() {
for (let i = 0; i < particlesArray.length; i++) {
if (mouse.x !== null && mouse.y !== null) {
const dx = mouse.x - particlesArray[i].x;
const dy = mouse.y - particlesArray[i].y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 当粒子进入鼠标影响范围时,使其远离鼠标
if (distance < mouse.radius) {
const force = (mouse.radius - distance) / mouse.radius;
const angle = Math.atan2(dy, dx);
particlesArray[i].x -= Math.cos(angle) * force * 2;
particlesArray[i].y -= Math.sin(angle) * force * 2;
}
}
}
// 循环调用交互检测
requestAnimationFrame(interactWithMouse);
}
// 启动动画
init();
animate();
interactWithMouse();
</script>
</body>
</html>