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

纯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实体写法:
    <div>
      <a href="#" class="nav__logo"><b>@alexpoov </b></a>
      <span class="nav__logo txt-type" data-wait="3000" data-words='["🌸", "🤙", "✌", "💻", "☔", "🎹", "📸", "🤑"]'> 
      </span>
    </div>
    
    如果不方便直接写入Emoji,可在JS初始化阶段统一解码所有实体,不需要修改HTML:
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 03:36:25