js实现星空动画

星空

灵感来自 lobehub img.png

Canvas实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>星空动画</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            display: block;
        }
    </style>
</head>

<body>
<canvas id="starry-sky"></canvas>
<script>
    const canvas = document.getElementById('starry-sky');
    const ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const numStars = 150;
    const stars = [];

    class Star {
        constructor() {
            this.x = Math.random() * canvas.width;
            this.y = Math.random() * canvas.height;
            this.size = (Math.random() * 3 + 1) * .4;
            const colors = [
                '#fff',
                '#f0f',
                // '#0ff',
                '#ff0'
            ];
            this.color = colors[Math.floor(Math.random() * colors.length)];
            this.speedX = (Math.random() * .5 + 1) / 3
            this.speedY = (Math.random() * .5 + 1) / 3
            console.log(this.speedX, this.speedY)
        }

        move() {
            this.x += this.speedX;
            this.y -= this.speedY;
            if (this.x > canvas.width) {
                this.x = 0;
            }
            if (this.y < 0) {
                this.y = canvas.height;
            }
        }

        draw() {
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
            ctx.fillStyle = this.color;
            ctx.fill();
        }
    }

    function createStars() {
        for (let i = 0; i < numStars; i++) {
            stars.push(new Star());
        }
    }

    function drawBackground() {
        const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
        gradient.addColorStop(0, '#000000');
        gradient.addColorStop(.25, 'rgb(21,18,27)');
        gradient.addColorStop(.5, 'rgba(32,28,42,1)');
        gradient.addColorStop(1, 'rgb(44,52,99)');
        // gradient.addColorStop(1, '#445299');
        // gradient.addColorStop(1, '#1a1a4d');
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawBackground();
        stars.forEach(star => {
            star.move();
            star.draw();
        });
        requestAnimationFrame(animate);
    }

    createStars();
    animate();

    // window.addEventListener('resize', () => {
    //     canvas.width = window.innerWidth;
    //     canvas.height = window.innerHeight;
    // });
</script>
</body>

</html>
## Dom实现
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>星空动画</title>
    <style>
        html, body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        #starry-sky {
            width: 100vw;
            height: 100vh;
            background: linear-gradient(to bottom, #000000, rgb(21, 18, 27), rgba(32, 28, 42, 1), rgb(44, 52, 99));
            position: relative;
            overflow: hidden;
        }

        .star {
            border-radius: 50%;
            position: absolute;
            box-shadow: 0 0 5px #fff;
            transform: translateX(calc(1px * var(--x))) translateY(calc(1px * var(--y)));
            /*transition: transform 300ms linear;*/
        }
    </style>
</head>

<body>
<div id="starry-sky"></div>
<script>
    const starrySky = document.getElementById('starry-sky');
    const numStars = 200; // 星星数量
    const positionMap = new WeakMap()
    const { height, width } = starrySky.getBoundingClientRect();
    const stars = []

    function createStars() {
        for (let i = 0; i < numStars; i++) {
            const star = document.createElement('div');
            stars.push(star)
            star.classList.add('star');
            // 随机初始位置
            const x = Math.random() * width;
            const y = Math.random() * height;
            positionMap.set(star, { x, y })
            // star.style.left = x + 'px';
            // star.style.top = y + 'px';
            // 随机大小
            const size = Math.random() * 3 + 1;
            star.style.width = size + 'px';
            star.style.height = size + 'px';
            // 随机颜色
            const colors = ['#fff', '#f0f', '#0ff', '#ff0'];
            const randomColor = colors[Math.floor(Math.random() * colors.length)];
            star.style.backgroundColor = randomColor;
            // 随机移动速度
            const speedX = (Math.random() * 2 + 1) / 3;
            const speedY = (Math.random() * 2 + 1) / 3;
            star.dataset.speedX = speedX;
            star.dataset.speedY = speedY;
            starrySky.appendChild(star);
            setPos(star)
        }
    }

    let dt = 0

    function setPos(star) {
        let { x, y } = positionMap.get(star)
        x += parseFloat(star.dataset.speedX) * dt;
        y -= parseFloat(star.dataset.speedY) * dt;
        if (x > width) x -= width
        if (y < 0) y += height

        positionMap.set(star, { x, y })
        star.style.setProperty('--y', y);
        star.style.setProperty('--x', x);
    }
    let now = performance.now()
    function moveStars() {
        // 降低帧率不稳定导致的抖动
        const current = performance.now()
        dt = (current - now) / 15
        now = current
        stars.forEach((star) => {
            setPos(star)
        });
        requestAnimationFrame(moveStars)
    }

    createStars();
    moveStars()
    // setInterval(moveStars, 50);
</script>
</body>

</html>
updatedupdated2025-09-302025-09-30