boxmoe_header_banner_img

Hello! Welcome To Eason's Blog!

What’s this post about?

Dancing Dots in the Void


avatar
Moontide 2025-04-12 137

Recently, I had some free time and created a fun interactive background effect for websites. The main idea is to use the <canvas> element to draw a large number of freely moving particles in the background. When the mouse gets close to a particle, it interacts smoothly with the cursor, creating a silky and dynamic experience.

Dynamic Particle Effects

  • Instead of disappearing when reaching the edge of the screen, particles reappear from the opposite side, creating a seamless wrap-around effect.
  • Each particle has its own random initial position, speed, and size.
  • When particles are close to each other, semi-transparent lines are automatically drawn between them, forming a web-like visual structure.

The entire dynamic background is implemented purely with JavaScript—lightweight, efficient, and easy to integrate into almost any webpage.



Demo Link Demo Resource

HTML代码

<!DOCTYPE html>

<html lang=”zh”>

<head>

    <meta charset=”UTF-8″>

    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>

    <title>粒子背景效果</title>

    <link rel=”stylesheet” href=”style.css”>

</head>

<body>

    <!– 粒子背景 –>

    <div id=”particles-container”></div>

    <!– 粒子脚本 –>

    <script src=”script.js”></script>

</body>

</html>

CSS代码

/* 基础样式 */

:root {

    –bg-color: #222;

}

* {

    margin: 0;

    padding: 0;

    box-sizing: border-box;

}

body {

    background-color: var(–bg-color);

    overflow: hidden;

    width: 100vw;

    height: 100vh;

}

/* 粒子背景 */

#particles-container {

    position: fixed;

    top: 0;

    left: 0;

    width: 100%;

    height: 100%;

    z-index: -1;

}

JS代码

document.addEventListener(‘DOMContentLoaded’, function () {

    const particlesContainer = document.getElementById(‘particles-container’);

    const particles = [];

    const particleCount = 100;

    const colors = [‘#aaa’, ‘#bbb’, ‘#ccc’, ‘#ddd’];

    const mousePosition = { x: null, y: null };

    document.addEventListener(‘mousemove’, function (event) {

        mousePosition.x = event.clientX;

        mousePosition.y = event.clientY;

    });

    document.addEventListener(‘mouseleave’, function () {

        mousePosition.x = null;

        mousePosition.y = null;

    });

    class Particle {

        constructor(canvas) {

            this.init(canvas);

        }

        init(canvas) {

            this.x = Math.random() * window.innerWidth;

            this.y = Math.random() * window.innerHeight;

            this.size = Math.random() * 3 + 1;

            this.baseSpeedX = (Math.random() – 0.5) * 0.5;

            this.baseSpeedY = (Math.random() – 0.5) * 0.5;

            this.speedX = this.baseSpeedX;

            this.speedY = this.baseSpeedY;

            this.color = colors[Math.floor(Math.random() * colors.length)];

            this.canvas = canvas;

            this.ctx = canvas.getContext(‘2d’);

            this.recovered = true;

        }

        update() {

            this.x += this.speedX;

            this.y += this.speedY;

            if (this.x < 0) this.x = window.innerWidth;

            else if (this.x > window.innerWidth) this.x = 0;

            if (this.y < 0) this.y = window.innerHeight;

            else if (this.y > window.innerHeight) this.y = 0;

            let influenced = false;

            if (mousePosition.x && mousePosition.y) {

                const dx = mousePosition.x – this.x;

                const dy = mousePosition.y – this.y;

                const distance = Math.sqrt(dx * dx + dy * dy);

                if (distance < 100) {

                    const angle = Math.atan2(dy, dx);

                    const force = (100 – distance) / 1500;

                    this.speedX -= Math.cos(angle) * force;

                    this.speedY -= Math.sin(angle) * force;

                    influenced = true;

                    this.recovered = false;

                }

            }

            if (!influenced && !this.recovered) {

                const dSpeedX = this.baseSpeedX – this.speedX;

                const dSpeedY = this.baseSpeedY – this.speedY;

                this.speedX += dSpeedX * 0.01;

                this.speedY += dSpeedY * 0.01;

                if (Math.abs(dSpeedX) < 0.01 && Math.abs(dSpeedY) < 0.01) {

                    this.speedX = this.baseSpeedX;

                    this.speedY = this.baseSpeedY;

                    this.recovered = true;

                }

            }

            const maxSpeed = 2;

            const currentSpeed = Math.sqrt(this.speedX ** 2 + this.speedY ** 2);

            if (currentSpeed > maxSpeed) {

                this.speedX = (this.speedX / currentSpeed) * maxSpeed;

                this.speedY = (this.speedY / currentSpeed) * maxSpeed;

            }

        }

        draw() {

            this.ctx.beginPath();

            this.ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);

            this.ctx.fillStyle = this.color;

            this.ctx.fill();

        }

    }

    const canvas = document.createElement(‘canvas’);

    canvas.width = window.innerWidth;

    canvas.height = window.innerHeight;

    particlesContainer.appendChild(canvas);

    const ctx = canvas.getContext(‘2d’);

    for (let i = 0; i < particleCount; i++) {

        particles.push(new Particle(canvas));

    }

    window.addEventListener(‘resize’, function () {

        canvas.width = window.innerWidth;

        canvas.height = window.innerHeight;

        particles.forEach(p => {

            p.x = Math.random() * window.innerWidth;

            p.y = Math.random() * window.innerHeight;

        });

    });

    function animate() {

        ctx.clearRect(0, 0, canvas.width, canvas.height);

        particles.forEach(p => {

            p.update();

            p.draw();

        });

        connectParticles();

        if (particles.length < particleCount) {

            for (let i = particles.length; i < particleCount; i++) {

                particles.push(new Particle(canvas));

            }

        }

        requestAnimationFrame(animate);

    }

    function connectParticles() {

        for (let i = 0; i < particles.length; i++) {

            for (let j = i + 1; j < particles.length; j++) {

                const dx = particles[i].x – particles[j].x;

                const dy = particles[i].y – particles[j].y;

                const distance = Math.sqrt(dx * dx + dy * dy);

                if (distance < 120) {

                    const opacity = 1 – (distance / 120);

                    ctx.beginPath();

                    ctx.strokeStyle = `rgba(200, 200, 200, ${opacity * 0.2})`;

                    ctx.lineWidth = 0.5;

                    ctx.moveTo(particles[i].x, particles[i].y);

                    ctx.lineTo(particles[j].x, particles[j].y);

                    ctx.stroke();

                }

            }

        }

    }

    animate();

});


 

Web


Comments(0)

View Comment List

So quiet here… No comments yet!


Post Comment