如何淡入 div 中的文本,同时解扰 JavaScript 中的文本

How do I fade in text in a div, while simultaneously descrambling the text in JavaScript

如何在动画转换期间对文本执行动画转换,同时执行淡入效果?

        // ——————————————————————————————————————————————————
        // TextScramble
        // ——————————————————————————————————————————————————

        class TextScramble {
            constructor(elm, numWords) {
                this.el = el
                this.numWords = numWords;
                this.chars = '!<>-_\/[]{}—=+*^?#1234567890________'
                this.update = this.update.bind(this)
            }
            setText(newText) {
                const oldText = this.el.innerText
                const length = Math.max(oldText.length, newText.length)
                const promise = new Promise((resolve) => this.resolve = resolve)
                this.queue = []
                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)
                    this.queue.push({ from, to, start, end })
                }
                cancelAnimationFrame(this.frameRequest)
                this.frame = 0
                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
        let animation = phraseValues[0];

        let animate = () => {
            return function(callback) {
            document.querySelector(".text").animate([
                // keyframes
                { opacity: '0' },
                { opacity: '1' }
                    ], {
                    // timing options
                    duration: 3500,
                    iterations: 1
                });
                callback();
            }
        }   

        const next = () => {
        fx.setText(phraseValues[counter]).then(() => {
                if (counter <= phraseValues.length)
                    setTimeout(next, 800)
                else {
                    animation = phrases[phraseValues[counter]]
                    setTimeout(animate(next), 800)
                }
            })
            counter = (counter + 1) % phraseValues.length
        }
        next()
html, body {
            font-family: 'Roboto Mono', monospace;
            background: #212121;
            height: 100%;
        }
        .container {
            height: 100%;
            width: 100%;
            justify-content: center;
            align-items: center;
            display: flex;
        }
        .text {
            font-weight: 100;
            font-size: 28px;
            color: #FAFAFA;
        }
        .dud {
            color: #757575;
        }
        .fadeIn {
            animation: fade 10s;
        }
 <div class="container">
        <div class="text"></div>
    </div>

我非常接近:

我将回调调用语句移到其下方的闭包 return 语句上方。我保留了动画关键帧。

最后我修改了要动画的语句中的setTimeout,如下:

我将 next 和 animate 作为回调一起放在 setTimeout 中,以便按级联顺序回调。

我使用了一个使用箭头符号的匿名函数定义。

在 animate(我写的函数)上,我传入 fx.update 函数定义作为回调,并调用闭包函数 returned animate 作为高阶函数,就像这样。

查看结果!

// ——————————————————————————————————————————————————
            // TextScramble
            // ——————————————————————————————————————————————————
    
            class TextScramble {
                constructor(elm, numWords) {
                    this.el = el
                    this.numWords = numWords;
                    this.chars = '!<>-_\/[]{}—=+*^?#1234567890________'
                    this.update = this.update.bind(this)
                }
                setText(newText) {
                    const oldText = this.el.innerText
                    const length = Math.max(oldText.length, newText.length)
                    const promise = new Promise((resolve) => this.resolve = resolve)
                    this.queue = []
                    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)
                        this.queue.push({ from, to, start, end })
                    }
                    cancelAnimationFrame(this.frameRequest)
                    this.frame = 0
                    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="span">${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
            let animation = phraseValues[0];
    
            let animate = (callback) => {
                callback();
                return function() {
                    document.querySelector(".text").animate([
                       // keyframes
                       { opacity: '0' },
                       { opacity: '1' }
                    ], {
                        // timing options
                        duration: 3500
                    });
                }
            }   
    
            const next = () => {
            fx.setText(phraseValues[counter]).then(() => {
                    if (counter < phraseValues.length-1)
                        setTimeout(next, 800)
                    else {
                        setTimeout(() => {next, animate(next, fx.update)()}, 800)
                    }
                })
                counter = (counter + 1) % phraseValues.length
            }
            next()
html, body {
            font-family: 'Roboto Mono', monospace;
            background: #212121;
            height: 100%;
        }
        .container {
            height: 100%;
            width: 100%;
            justify-content: center;
            align-items: center;
            display: flex;
        }
        .text {
            font-weight: 100;
            font-size: 28px;
            color: #FAFAFA;
        }
        .dud {
            color: #757575;
        }
<div class="container">
        <div class="text"></div>
 </div>