Skip to content

Instantly share code, notes, and snippets.

@jerrylususu
Last active September 6, 2024 15:38
Show Gist options
  • Save jerrylususu/a67e0d7973bcdec352753b14b3436524 to your computer and use it in GitHub Desktop.
Save jerrylususu/a67e0d7973bcdec352753b14b3436524 to your computer and use it in GitHub Desktop.
document.addEventListener('DOMContentLoaded', function() {
const c = document.getElementById("c");
if (!c) {
console.error("Canvas element with id 'c' not found");
return;
}
const ctx = c.getContext("2d");
if (!ctx) {
console.error("Unable to get 2D context for canvas");
return;
}
c.height = window.innerHeight;
c.width = window.innerWidth;
const txts = "ABCFGHKLMNPQRSVWXYZabcdefghijklmnopqrstuvwxyz@#$%&*+-;/[]<>~".split("");
const font_size = 12;
const columns = Math.floor(c.width / font_size);
const drops = new Array(columns).fill(1);
// Create an off-screen canvas
const offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = c.width;
offScreenCanvas.height = c.height;
const offScreenCtx = offScreenCanvas.getContext('2d');
// Pre-render characters
const charCache = {};
txts.forEach(char => {
const charCanvas = document.createElement('canvas');
charCanvas.width = font_size;
charCanvas.height = font_size;
const charCtx = charCanvas.getContext('2d');
charCtx.font = font_size + "px arial";
charCtx.fillStyle = "#490";
charCtx.fillText(char, 0, font_size);
charCache[char] = charCanvas;
});
let lastTime = 0;
const fps = 30;
const interval = 1000 / fps;
function draw(currentTime) {
requestAnimationFrame(draw);
if (currentTime - lastTime < interval) return;
lastTime = currentTime;
offScreenCtx.fillStyle = "rgba(0, 0, 0, 0.05)";
offScreenCtx.fillRect(0, 0, c.width, c.height);
for (let i = 0; i < drops.length; i++) {
if (drops[i] * font_size > c.height || Math.random() > 0.98) {
drops[i] = 0;
} else {
const text = txts[Math.floor(Math.random() * txts.length)];
offScreenCtx.drawImage(charCache[text], i * font_size, drops[i] * font_size);
drops[i]++;
}
}
// Copy the off-screen canvas to the main canvas
ctx.drawImage(offScreenCanvas, 0, 0);
}
requestAnimationFrame(draw);
});
// Handle window resize
window.addEventListener('resize', function() {
const c = document.getElementById("c");
if (c) {
c.height = window.innerHeight;
c.width = window.innerWidth;
// You might want to reinitialize your effect here
}
});
@jerrylususu
Copy link
Author

感谢您分享这段代码。我理解您遇到了性能问题,让我们一起来分析和优化这个黑客帝国风格的文字滚动特效。

首先,让我们看看造成性能问题的主要原因:

  1. 使用 setInterval 以固定的高频率(每20毫秒)调用 draw 函数。
  2. 在每次绘制时都重新绘制整个画布。
  3. 大量的随机数生成和数组操作。

下面是一些优化建议,我会在代码中实现这些改进:

  1. 使用 requestAnimationFrame 代替 setInterval,这样可以更好地与浏览器的渲染周期同步。
  2. 减少不必要的重绘,只更新发生变化的部分。
  3. 减少随机数生成的频率,将一些计算结果缓存起来。
  4. 使用离屏canvas进行绘制,然后一次性将结果复制到主canvas上。
  5. 优化循环和条件判断,减少不必要的计算。

下面是优化后的代码:

const c = document.getElementById("c");
const ctx = c.getContext("2d");
c.height = window.innerHeight;
c.width = window.innerWidth;

const txts = "ABCFGHKLMNPQRSVWXYZabcdefghijklmnopqrstuvwxyz@#$%&*+-;/[]<>~".split("");
const font_size = 12;
const columns = Math.floor(c.width / font_size);
const drops = new Array(columns).fill(1);

// Create an off-screen canvas
const offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = c.width;
offScreenCanvas.height = c.height;
const offScreenCtx = offScreenCanvas.getContext('2d');

// Pre-render characters
const charCache = {};
txts.forEach(char => {
    const charCanvas = document.createElement('canvas');
    charCanvas.width = font_size;
    charCanvas.height = font_size;
    const charCtx = charCanvas.getContext('2d');
    charCtx.font = font_size + "px arial";
    charCtx.fillStyle = "#490";
    charCtx.fillText(char, 0, font_size);
    charCache[char] = charCanvas;
});

let lastTime = 0;
const fps = 30;
const interval = 1000 / fps;

function draw(currentTime) {
    requestAnimationFrame(draw);

    if (currentTime - lastTime < interval) return;
    lastTime = currentTime;

    offScreenCtx.fillStyle = "rgba(0, 0, 0, 0.05)";
    offScreenCtx.fillRect(0, 0, c.width, c.height);

    for (let i = 0; i < drops.length; i++) {
        if (drops[i] * font_size > c.height || Math.random() > 0.98) {
            drops[i] = 0;
        } else {
            const text = txts[Math.floor(Math.random() * txts.length)];
            offScreenCtx.drawImage(charCache[text], i * font_size, drops[i] * font_size);
            drops[i]++;
        }
    }

    // Copy the off-screen canvas to the main canvas
    ctx.drawImage(offScreenCanvas, 0, 0);
}

requestAnimationFrame(draw);

这个优化后的版本做了以下改进:

  1. 使用 requestAnimationFrame 代替 setInterval,并控制帧率为30fps。
  2. 创建了一个离屏canvas用于绘制,然后一次性将结果复制到主canvas上,减少了主线程的绘制负担。
  3. 预渲染了所有字符,存储在 charCache 中,避免了重复绘制字符。
  4. 使用 drawImage 来绘制预渲染的字符,比 fillText 更快。
  5. 优化了数组初始化和循环结构。

这些优化应该能显著提高性能,减少CPU的使用。但是,如果您仍然遇到性能问题,还可以考虑以下进一步的优化:

  1. 减少绘制的字符数量或增加字符大小。
  2. 降低动画的帧率,比如将fps设置为24或更低。
  3. 考虑使用WebGL来进行渲染,它可以利用GPU加速。

如果您想进一步优化或有任何问题,请随时告诉我。我们可以一起探讨更多的优化策略。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment