完整代码

class CodeRain {

    constructor(option = {}) {
        this.option = {
            // 父元素
            elem: 'body',
            // 字体大小
            fontSize: 20,
            // 速度 (ms)
            speed: 20,
            // 尾巴长度 0-1  越小尾巴越短
            tail: 0.9,
            // 绘制文字, 可以是每次生成一个随机字符或者固定字符
            text: () => this.#randStr(),
            // 字体颜色, 可以是函数生成随机颜色或者固定
            color: () => this.#randColor(),
            // canvsa背景颜色
            bgColor: `#000`,
            // 是否第一幕扫描效果下来
            isScan: true,
            ...option
        };
        this.option.elem = typeof this.option.elem === 'string' ? document.querySelector(this.option.elem) : this.option.elem;
        if (!this.option.elem.tagName) {
            throw new Error(`[elem]父元素  参数错误`);
        }
        this.width = this.option.elem.offsetWidth;
        this.height = this.option.elem.offsetHeight;
        this.fontSize = this.option.fontSize * devicePixelRatio;
        this.colWidth = this.fontSize;
        this.colCount = Math.floor(this.width / this.colWidth);
        this.nextRow = new Array(this.colCount).fill(0);
        if (!this.option.isScan) {
            for (let k in this.nextRow) {
                if (Math.random() > 0.2) {
                    this.nextRow[k] = -Math.floor(Math.random() * (this.height / this.fontSize));
                }
            }
        }
        this.timer = null;

        this.#init();
    }

    /**
     * @description: 开始绘制
     * @return {CodeRain}
     */
    run() {
        this.timer = setInterval(() => {
            this.#draw();
        }, this.option.speed);
        return this;
    }

    /**
     * @description: 停止动画
     * @return {void}
     */
    stop() {
        if (this.timer) {
            clearInterval(this.timer);
        } else {
            console.error('[CodeRain] 未在运行');
        }
    }

    /**
     * @description: 初始化
     * @return {*}
     */
    #init() {
        this.canvas = document.createElement('canvas');
        this.canvas.setAttribute('width', this.width);
        this.canvas.setAttribute('height', this.height);
        this.option.elem.appendChild(this.canvas);
        this.ctx = this.canvas.getContext('2d');
        this.ctx.fillStyle = this.option.bgColor;
        this.ctx.fillRect(0, 0, this.width, this.height);
    }

    /**
     * @description: 绘画一次
     * @return {void}
     */
    #draw() {
        this.ctx.fillStyle = this.#colorToRgba(this.option.bgColor, 1 - this.option.tail);
        this.ctx.fillRect(0, 0, this.width, this.height);
        for (let i = 0; i < this.colCount; i++) {
            const str = typeof this.option.text === 'function' ? this.option.text() : this.option.text;
            const color = typeof this.option.color === 'function' ? this.option.color() : this.option.color
            this.ctx.fillStyle = color;
            // 等宽字体
            this.ctx.font = `${this.fontSize}px "Roboto Mono"`;
            const x = this.colWidth * i;
            const y = this.fontSize * (this.nextRow[i] + 1);
            this.ctx.fillText(str, x, y);
            if (y > this.height && Math.random() > 0.99) {
                this.nextRow[i] = 0;
            } else {
                this.nextRow[i]++;
            }
        }
    }

    /**
     * @description: 随机一个字符串
     * @return {string}
     */
    #randStr() {
        const str = `abcdefghijklmnopqrstuvwxyz0123456789".,;{}[]-_=+!#$&*`;
        return str[Math.floor(Math.random() * str.length)];
    }

    /**
     * @description: 随机颜色
     * @return {string} 颜色hex
     */
    #randColor() {
        return `#${Math.random().toString(16).substring(3, 9)}`;
    }

    /**
     * @description: 颜色转rgba
     * @param {string} color 颜色值
     * @param {number} alpha 透明度 0-1
     * @return {string} rgba
     */
    #colorToRgba(color, alpha = 1) {
        let r, g, b;

        if (color.startsWith('#')) {
            if (color.length === 4) {
                color = `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`;
            }
            r = parseInt(color.slice(1, 3), 16);
            g = parseInt(color.slice(3, 5), 16);
            b = parseInt(color.slice(5, 7), 16);
        } else if (color.startsWith('rgb')) {
            const rgbValues = color.match(/\d+/g).map(Number);
            r = rgbValues[0];
            g = rgbValues[1];
            b = rgbValues[2];
        } else if (color.startsWith('rgba')) {
            const rgbaValues = color.match(/\d+/g).map(Number);
            r = rgbaValues[0];
            g = rgbaValues[1];
            b = rgbaValues[2];
        } else if (color.startsWith('hsl')) {
            const hslValues = color.match(/\d+/g).map(Number);
            const h = hslValues[0];
            const s = hslValues[1] / 100;
            const l = hslValues[2] / 100;

            const c = (1 - Math.abs(2 * l - 1)) * s;
            const x = c * (1 - Math.abs((h / 60) % 2 - 1));
            const m = l - c / 2;

            if (h < 60) {
                r = c; g = x; b = 0;
            } else if (h < 120) {
                r = x; g = c; b = 0;
            } else if (h < 180) {
                r = 0; g = c; b = x;
            } else if (h < 240) {
                r = 0; g = x; b = c;
            } else if (h < 300) {
                r = x; g = 0; b = c;
            } else {
                r = c; g = 0; b = x;
            }

            r = Math.round((r + m) * 255);
            g = Math.round((g + m) * 255);
            b = Math.round((b + m) * 255);
        } else {
            throw new Error(`[${color}] 不支持这个颜色格式`);
        }

        return `rgba(${r}, ${g}, ${b}, ${alpha})`;
    }
}

使用方法

new CodeRain({
  elem: '#codeRainBox',
  color: 'green',
}).run();

实现效果