优化 JavaScript 动画的性能:带有画布和像素的星落?

问题描述 投票:0回答:1

我有一个 JavaScript 动画,它使用 HTML 画布和“像素”操作来模拟星落效果。虽然动画有效,但我面临效率和速度问题,并且我正在寻找优化代码的方法。该动画涉及创建从屏幕顶部落下的星星,并在移动时留下像素轨迹。问题是,像素是

divs
,达到了数千。

当前JS代码:

const content = document.getElementById("bkg-content");
const background = document.getElementById("bkg");
const ctx = content.getContext("2d", { willReadFrequently: true });

let stars = [];
const maxStars = 20; // Maximum number of stars

let last_x = 0;
let next_spawn = 0;

function getX(size) {
    let random = Math.random() * (window.innerWidth - size);
    return last_x - size > random || random > last_x + size ? random : getX(size);
}

function createStar() {
    let transparency = Math.random();
    let size = transparency * 200 + 100;
    let x = getX(size)
    let fallingSpeed = Math.random() * 30 + 20;
    next_spawn = size / fallingSpeed * 500;

    last_x = x;
    return {
        x,
        y: 0,
        size,
        transparency,
        rotationAngle: 0,
        rotationSpeed: Math.random() * 0.3 + 0.05,
        rotationDirection: Math.random() < 0.5 ? 1 : -1,
        fallingSpeed,
    };
}

function spawnStars(count) {
    for (let i = 0; i < count; i++) {
        stars.push(createStar());
    }

    setTimeout(() => {
        spawnStars(1)
    }, next_spawn)

    console.log(stars.length);
}

function draw() {
    ctx.clearRect(0, 0, content.width, content.height);

    for (let i = 0; i < stars.length; i++) {
        let star = stars[i];
        star.rotationAngle += star.rotationSpeed * star.rotationDirection;
        star.y += star.fallingSpeed;

        if (star.y > window.innerHeight + star.size / 2) {
            // Remove stars that have completely passed the bottom of the screen
            stars.splice(i, 1);
            i--; // Adjust the loop variable since the array length has changed
        } else {
            ctx.save();
            ctx.translate(star.x + star.size / 2, star.y);
            ctx.rotate(star.rotationAngle);

            // Adjust transparency based on star size
            ctx.globalAlpha = star.transparency;
            console.log(ctx.globalAlpha)

            ctx.drawImage(starTXT, -star.size / 2, -star.size / 2, star.size, star.size);

            // Reset global alpha for subsequent drawings
            ctx.globalAlpha = 1;
            ctx.restore();
        }
    }
}


function resizeCanvas() {
    content.width = window.innerWidth;
    content.height = window.innerHeight;

    spawnStars(Math.min(maxStars, 1)); // Initially spawn one star

    setInterval(() => {
        createPixels()
        draw()
    }, 500);
}

function createPixels() {
    const pixel = { size: 9, gap: 3 };
    const numX = Math.floor(window.innerWidth / (pixel.size + pixel.gap));
    const numY = Math.floor(window.innerHeight / (pixel.size + pixel.gap));

    background.style.gridTemplateColumns = `repeat(${numX}, ${pixel.size}px)`;
    background.innerHTML = ''; // clear existing pixels

    for (let x = 0; x < numX; x++) {
        for (let y = 0; y < numY; y++) {
            const child = document.createElement("div");
            child.classList.add("pixel");
            background.appendChild(child);
        }
    }

    const children = Array.from(background.getElementsByClassName("pixel"));

    children.forEach((child) => {
        const rect = child.getBoundingClientRect();
        const { data } = ctx.getImageData(rect.left, rect.top, pixel.size, pixel.size);

        const averageColor = getAverageColor(data);
        const color = `rgb(${averageColor}, ${averageColor}, ${averageColor})`;

        child.style.backgroundColor = color;
        child.style.boxShadow = `0 0 10px ${color}`;
    });
}

function getAverageColor(data) {
    let sum = 0;

    for (let i = 0; i < data.length; i += 4) {
        sum += data[i];
    }


    const averageColor = sum / (data.length / 4);

    return averageColor;
}

const starTXT = new Image(1600, 1600);
starTXT.src = "star.png";

starTXT.onload = resizeCanvas;

window.addEventListener('resize', resizeCanvas);

另一个问题是调整大小,以便它是最佳且快速的,代码滞后很多。有小费吗?我可以采取任何不同的方法吗?

the result I aimed for

当前的实现似乎存在性能瓶颈。我怀疑绘制函数中的循环和画布的频繁操作可能会导致速度减慢。如何优化代码以提高效率?动画速度对于流畅的用户体验至关重要。我应该遵循什么特定的技术或最佳实践来提高星坠效果的速度?我使用画布绘制星星和像素操作背景的组合。这是最佳方法,还是有可以提供更好性能的替代方法?

我已经尝试过一些基本的优化,例如使用 requestAnimationFrame 而不是 setInterval,但我正在寻找来自社区的更多见解和建议。任何有助于识别和解决代码中的性能问题的帮助将不胜感激。

抱歉,如果我的代码有任何混乱或不可读,我已经尝试了很多。

编辑:HTML 文件在下面

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>...</title>

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            height: 100vh;
            background-color: black;
            overflow: hidden;
            /* Prevent scrolling */
        }

        #bkg-content {
            width: 100%;
            height: 100%;
            position: absolute;
            z-index: 1;
            opacity: 0%;
        }

        #bkg {
            width: 100%;
            height: 100%;
            display: grid;
            gap: 3px;
            position: absolute;
            z-index: 2;
        }

        .pixel {
            width: 9px;
            height: 9px;
        }
    </style>
</head>

<body>
    <canvas id="bkg-content"></canvas>
    <div id="bkg"></div>
    <script src="./script.js"></script>
</body>

</html>
javascript html css loops background
1个回答
0
投票

获得良好动画速度的最佳方法是通过 CSS 动画来实现,而不是尝试使用 JS 来实现。

CSS 动画的介绍有点超出了答案,所以这里是 MDN 上对该主题的介绍。

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations

© www.soinside.com 2019 - 2024. All rights reserved.