纯JS实现Emoji打字动画时菱形问号渲染异常问题求解
问题根因
带问号的菱形占位符(Unicode替换字符)和十进制/十六进制渲染特性无关,是三个实现层面的问题导致的:
- 实体拼接逻辑错误:
data-words中存储的是HTML转义后的Emoji实体(如🌻),打字动画逐字符追加内容时,在结尾分号被追加前,字符串是不完整的HTML实体,浏览器无法解析为有效Emoji,就会渲染占位符,直到完整实体拼接完成才会显示正常Emoji,产生闪烁。 - 字体回退延迟:全局设置的
Raleway字体不含Emoji字形,浏览器渲染Emoji时需要逐层回退查找系统内置Emoji字体,这个查找过程会带来短暂的渲染空窗。 - 多码点Emoji截断异常:不少Emoji由2个及以上Unicode码元组成(如带肤色修饰的Emoji、组合类Emoji),原有逻辑用
substring按单个UTF-16码元截断/追加,会拆分出不完整的码元序列,无法正常渲染。
修复方案
按以下步骤调整即可彻底解决占位符闪烁问题:
- 第一步:预解码Emoji,避免逐字拼接不完整实体
直接在data-words中写入原生Emoji字符,替换原有HTML实体写法:
如果不方便直接写入Emoji,可在JS初始化阶段统一解码所有实体,不需要修改HTML:<div> <a href="#" class="nav__logo"><b>@alexpoov </b></a> <span class="nav__logo txt-type" data-wait="3000" data-words='["🌸", "🤙", "✌", "💻", "☔", "🎹", "📸", "🤑"]'> </span> </div>function init() { const txtElement = document.querySelector('.txt-type'); const words = JSON.parse(txtElement.getAttribute('data-words')).map(word => { const textarea = document.createElement('textarea'); textarea.innerHTML = word; return textarea.value; }); const wait = txtElement.getAttribute('data-wait'); new TypeWriter(txtElement, words, wait); } - 第二步:配置明确的Emoji字体栈,消除回退延迟
为打字效果元素指定优先匹配系统Emoji字体的字体栈,避免浏览器逐层查找字体的延迟:.txt-type { font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; } - 第三步:修正字符截断逻辑,适配多码点Emoji
原有逐字符截取逻辑会拆分多码点Emoji,改用Unicode字符迭代的方式拆分完整字符,避免截断异常,替换TypeWriter类中的type方法即可:type() { const current = this.wordIndex % this.words.length; const fullTxt = this.words[current]; // 拆分为完整Unicode字符数组,避免拆断多码点Emoji const fullChars = Array.from(fullTxt); const currentChars = Array.from(this.txt); if(this.isDeleting) { this.txt = fullChars.slice(0, currentChars.length - 1).join(''); } else { this.txt = fullChars.slice(0, currentChars.length + 1).join(''); } this.txtElement.innerHTML = `<span class="txt">${this.txt}</span>`; let typeSpeed = 300; if(this.isDeleting) { typeSpeed /= 2; } if(!this.isDeleting && this.txt === fullTxt) { typeSpeed = this.wait; this.isDeleting = true; } else if(this.isDeleting && this.txt === '') { this.isDeleting = false; this.wordIndex++; typeSpeed = 500; } setTimeout(() => this.type(), typeSpeed); } - 可选优化:预加载Emoji字形
可以在页面初始化时把所有用到的Emoji放到离屏隐藏元素中,触发浏览器提前加载Emoji字形,进一步消除首次渲染延迟:.emoji-preload { position: absolute; left: -9999px; visibility: hidden; }
内容的提问来源于stack exchange,提问作者Alex Popov
相关产品推荐
相关产品推荐

