You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JavaScript实现div内文本淡入同步执行文本解扰动画效果

调整方案

核心修改点如下:

  • 修复TextScramble构造函数参数拼写错误(原代码参数是elm但赋值时写了el,会直接报错)
  • 淡入动画触发时机从「解扰完成后」改为「解扰启动瞬间」,保证两者同步执行
  • 补全CSS中缺失的淡入关键帧定义
  • 修正动画流程逻辑,支持按phrases中的配置判断是否需要叠加淡入效果

修改后完整代码

JavaScript

// ——————————————————————————————————————————————————
// TextScramble
// ——————————————————————————————————————————————————
class TextScramble {
    constructor(elm, numWords) {
        // 修复参数拼写错误
        this.el = elm
        this.numWords = numWords;
        this.chars = '!<>-_\/[]{}—=+*^?#1234567890________'
        this.update = this.update.bind(this)
    }
    setText(newText, needFade = false) {
        const oldText = this.el.innerText
        const length = Math.max(oldText.length, newText.length)
        const promise = new Promise((resolve) => this.resolve = resolve)
        this.queue = []
        // 计算解扰总时长,用来匹配淡入时长
        let maxEndFrame = 0
        for (let i = 0; i < length; i++) {
            const from = oldText[i] || ''
            const to = newText[i] || ''
            const start = Math.floor(Math.random() * 40)
            const end = start + Math.floor(Math.random() * 40)
            maxEndFrame = Math.max(maxEndFrame, end)
            this.queue.push({ from, to, start, end })
        }
        cancelAnimationFrame(this.frameRequest)
        this.frame = 0

        // 解扰启动时同步触发淡入
        if (needFade) {
            this.el.animate([
                { opacity: '0' },
                { opacity: '1' }
            ], {
                // 按帧数换算时长,1帧约16.7ms,留少量冗余
                duration: maxEndFrame * 17,
                iterations: 1,
                fill: 'forwards'
            })
        } else {
            // 不需要淡入的直接设置不透明度为1
            this.el.style.opacity = 1
        }

        this.update()
        return promise
    }
    update() {
        let output = ''
        let complete = 0
        for (let i = 0, n = this.queue.length; i < n; i++) {
            let { from, to, start, end, char } = this.queue[i]
            if (this.frame >= end) {
                complete++
                output += to
            } else if (this.frame >= start) {
                if (!char || Math.random() < 0.28) {
                    char = this.randomChar()
                    this.queue[i].char = char
                }
                output += `<span class="dud">${char}</span>`
            } else {
                output += from
            }
        }
        this.el.innerHTML = output
        if (complete === this.queue.length) {
            this.resolve()
        } else {
            this.frameRequest = requestAnimationFrame(this.update)
            this.frame++
        }
    }
    randomChar() {
        return this.chars[Math.floor(Math.random() * this.chars.length)]
    }
}

// ——————————————————————————————————————————————————
// Example
// ——————————————————————————————————————————————————
const phrases = {
    'Coding' : 'none',
    'With' : 'none',
    'Muhammad': 'none',
    'Coding With Muhammad' : 'fade'
}
let phraseValues = Object.keys(phrases);
const el = document.querySelector('.text')
const fx = new TextScramble(el, phraseValues.length)

let counter = 0
const next = () => {
    const currentPhrase = phraseValues[counter]
    const needFade = phrases[currentPhrase] === 'fade'
    fx.setText(currentPhrase, needFade).then(() => {
        counter = (counter + 1) % phraseValues.length
        setTimeout(next, 800)
    })
}
next()

CSS

html, body {
    font-family: 'Roboto Mono', monospace;
    background: #212121;
    height: 100%;
    margin: 0;
}
.container {
    height: 100%;
    width: 100%;
    justify-content: center;
    align-items: center;
    display: flex;
}
.text {
    font-weight: 100;
    font-size: 28px;
    color: #FAFAFA;
    /* 初始不透明度设为0,避免首次加载闪一下 */
    opacity: 0;
}
.dud {
    color: #757575;
}
/* 补全淡入关键帧,备用 */
@keyframes fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
.fadeIn {
    animation: fade 10s forwards;
}

HTML

<div class="container">
    <div class="text"></div>
</div>

如果需要所有文本切换都叠加淡入效果,直接把phrases里所有值都改成fade即可,淡入时长会自动和当前文本的解扰时长匹配,也可以手动修改duration的数值自定义速度。

内容的提问来源于stack exchange,提问作者Vahe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 07:48:00