这个项目用了最原始的html,若是用vue应该更容易实现一些
效果如下:

要多次输入和回答,就需要动态插入dom节点,还要能区分开,就用了动态的id,直接看代码吧。

html部分:

<div class="output"></div>

css部分:

.typewriter {
    width: 100%;
    padding: 20px;
    margin: 0 auto;
    font-size: 16px;
    line-height: 1.6;
    font-family: 'Microsoft YaHei', sans-serif;
    background: #f8f9fa;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    word-wrap: break-word;
    overflow-wrap: break-word;
    white-space: pre-wrap;
  }
  .char {
    display: inline;
    opacity: 0;
    animation: appear 0.1s forwards;
  }
  @keyframes appear {
    to { opacity: 1; }
  }
.output .left {
    white-space: pre-wrap;
    word-wrap: break-word;
  }
  .output .right{
    display: flex;
    justify-content: right;
  }

js部分:

let askDom = '<div class="right">\n' +
            '              <div class="ask text-left">'+obj.labelArr+'</div>\n' +
            '            </div>'
    $(".output").append(askDom)

    let writeDom = '<div class="left text-left typewriter" id="typewriter'+obj.outIndex+'"></div>'
    $(".output").append(writeDom)

    const fullText = "老君曰:大道无形,生育天地;大道无情,运行日月;大道无名,长养万物;吾不知其名,强名曰道。夫道者:有清有浊,有动有静;天清地浊,天动地静;男清女浊,男动女静;降本流末,而生万物。清者,浊之源,动者,静之基;人能常清静,天地悉皆归。";

    let currentPos = 0;
    const typeInterval = setInterval(function() {
      if(currentPos >= fullText.length) {
        clearInterval(typeInterval);
        obj.outIndex++
        return;
      }

      const char = fullText.charAt(currentPos);
      const $char = $('<span>').addClass('char').text(char);
      $('#typewriter'+obj.outIndex).append($char);
      currentPos++;
    }, 50);

定义了一个对象属性控制id的编号

var obj = {
"outIndex":1
}

输出内容可以对接后端接口获取数据,基本能满足逐字输出、文字打印的需求了。