Skip to content

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>