打字機效果的實現與應用

前言

在 web 應用中,模擬編輯器或者模擬輸入框中文字啪啦啪啦輸入的效果,往往能夠吸引人們的眼球,讓用戶的注意力聚焦在輸入的內容上,其實使用的是 web 動畫模擬打字機效果,本文將和大家探討打字機效果的實現方式以及應用。

純 css 實現

最簡單的方式是莫過於直接使用 CSS 。大概思路是藉助 CSS3 的 @keyframe 動畫來不斷改變包含文字的容器的寬度,超出容器部分的文字隱藏不展示。

css 打字機效果

<style>
  .typing {
    font-size: 20px;
    /* 初始寬度爲0 */
    width: 0;
    height: 30px;
    border-right: 1px solid darkgray;
    animation: write 4s steps(14) forwards,
      blink 0.5s steps(1) infinite;
      overflow: hidden;
  }

  @keyframes write {
    0% {
      width: 0;
    }
    100% {
      width: 240px;
    }
  }

  @keyframes blink {
    50% {
      /* transparent是全透明黑色(black)的速記法,即一個類似rgba(0,0,0,0)這樣的值。 */
      border-color: transparent; /* #00000000 */
    }
  }
</style>

<body>
  <div>
    自在,輕盈,我本不想停留
  </div>
</body>

Steps(<number_of_steps>,<direction>)

animation steps 可以讓動畫斷斷續續,而非連續執行。接收兩個參數:

第一個參數指定動畫分割的段數;

第二個參數可選,接受 start 和 end 兩個值,指定在每個間隔的起點或是終點發生階躍變化,默認爲 end。

可以看到其實現原理很簡單,打字效果其實就是改變容器的寬度實現的。 初始文字是全部在頁面上的,只是容器的寬度爲 0,設置文字超出部分隱藏,然後不斷改變容器的寬度; 設置 border-right,並在關鍵幀上改變 border-colortransparent,右邊框就像閃爍的光標了。

優點是簡單,缺點也是有的,首先我們要先獲得文本的寬度,上面的截圖就是因爲寬度寫錯了,導致光標在文字後面,然後只支持 1 行。若想要支持多行,就得使用 JavaScript 了。

js 實現

setInterval 實現

<style>
  /* 產生光標閃爍的效果 */
  #content::after{
      content: '|';
      color:#000;
      animation: blink 1s infinite;
  }
  @keyframes blink{
      from{
          opacity: 0;
      }
      to{
          opacity: 1;
      }
  }
</style>
<div id='content'></div>
<script>
(function () {
  // 獲取容器
  const container = document.getElementById('content')
  // 把需要展示的全部文字進行切割
  const data = '自在,輕盈,我本不想停留'.split('')
  // 需要追加到容器中的文字下標
  let index = 0
  let timer = null
  function writing() {
    if (index < data.length) {
      // 追加文字
      container.innerHTML += data[index ++]
      // 也可以使用,clearTimeout取消setInterval的執行
      // index === 4 && clearTimeout(timer)
    } else {
      clearInterval(timer)
    }
    console.log(timer) // 這裏會打印出 1 1 1 1 1 ...
  }
  // 使用 setInterval 時,結束後不要忘記進行 clearInterval
  timer = setInterval(writing, 200)
})();
</script>

setInterval 版本的實現也很簡單,只需把要展示的文本進行切割,使用定時器不斷向 DOM 元素裏追加文字即可,同時使用::after 僞元素在 DOM 元素後面產生光標閃爍的效果。代碼和效果圖如下:

setTimeout 實現

和 setInterval 一樣,setTimeout 也可以實現

<style>
  /* 產生光標閃爍的效果 */
  #content::after{
      content: '|';
      color:#000;
      animation: blink 1s infinite;
  }
  @keyframes blink{
      from{
          opacity: 0;
      }
      to{
          opacity: 1;
      }
  }
</style>
  <div id='content'></div>
  <script>
    (function () {
    // 獲取容器
    const container = document.getElementById('content')
    // 把需要展示的全部文字進行切割
    const data = '自在,輕盈,我本不想停留'.split('')
    // 需要追加到容器中的文字下標
    let index = 0
    function writing() {
      if (index < data.length) {
        // 追加文字
        container.innerHTML += data[index ++]
        let timer = setTimeout(writing, 200)
      }
    }
    writing()
  })();
  </script>

需要強調一點:定時器指定的時間間隔,表示的是何時將定時器的代碼添加到消息隊列,而不是何時執行代碼,所以真正何時執行代碼的時間是不能保證的,取決於何時被主線程的事件循環取到,並執行。

那如果想要實現暫停和播放,那就必須使用 clearTimeout() 方法來終止,

<div id='content'></div>
<button id='pause'>暫停</button>
<script>
// 獲取容器
const container = document.getElementById('content')
// 把需要展示的全部文字進行切割
const data = '最簡單的打字機效果實現'.split('')
// 需要追加到容器中的文字下標
let index = 0
let timer

document.querySelector('#pause').addEventListener('click',()=>{
  clearTimeout(timer)
})

function writing() {
  if (index < data.length) {
    // 追加文字
    container.innerHTML += data[index ++]
    timer = setTimeout(writing, 200)
  }
}
writing()
</script>

但除了暫停,還有回退,修改等操作,需要修改光標位置等,我們可以使用一個 npm 庫來搞定。

Typeit 實現

Typeit 官網效果

首先需要引如 cdn script

<script src="https://unpkg.com/typeit@8.6.0/dist/index.umd.js"></script>

或是使用 npm 安裝這個包

npm install typeit

官網首頁 demo 代碼

new TypeIt("#hero", {
  speed: 50,
  startDelay: 900,
})
  .type("the mot versti", { delay: 100 })
  .move(-8, { delay: 100 })
  .type("s", { delay: 400 })
  .move(null, { to: "START", instant: true, delay: 300 })
  .move(1, { delay: 200 })
  .delete(1)
  .type("T", { delay: 225 })
  .pause(200)
  .move(2, { instant: true })
  .pause(200)
  .move(5, { instant: true })
  .move(5, { delay: 200 })
  .type("a", { delay: 350 })
  .move(null, { to: "END" })
  .type("le typing utlity")
  .move(-4, { delay: 150 })
  .type("i")
  .move(null, { to: "END" })
  .type(' on the <span>internet</span>', { delay: 400 })
  .delete(".place", { delay: 800, instant: true })
  .type('<em><strong>planet.</strong></em>', {
    speed: 100,
  })
  .go();

代碼一目瞭然,支持暫停、刪除,移動、而且還支持 html。

需要注意的是 TypeIt 在商用項目上是收費的, 在個人或者開源項目上是免費的。商用項目需要支付 $19, 那麼有沒有免費的呢?

typed.js 實現

那如果想在商用項目上免費使用,可以使用 typed.js ,採用 MIT 開源協議,與 TypeIt 類似的 api

<script>
  var typed = new Typed('#typed', {
    stringsElement: '#typed-strings'
  });
</script>
<div>
  <p>Typed.js is a <strong>JavaScript</strong> library.</p>
  <p>It <em>types</em> out sentences.</p>
</div>
<span></span>

對 seo 非常友好,它是在從頁面上的 HTML 元素讀取,再通過 js 動態插入。

打字機效果應用

程序講究的輸入和輸出,雖然我們在頁面上實現了動態輸入的效果,若能夠同步實現輸出,豈不是實現了編譯器的效果?

Sildev 使用 markdown 寫 PPT

之前分享的 Sildev[1],就完美地將輸入和輸出效果展現在頁面上

Sildev 輸入和輸出

源碼在這裏 [2]

new TypeIt(block.value, {
    speed: 50,
    startDelay: 900,
    afterStep: () => {
      code.value = JSON.parse(JSON.stringify(block.value!.innerText.replace('|', '')))
    },
  })
    .type('<br><span># 歡迎使用 Slidev!</span><br><br>', { delay: 400 })
    .type('爲開發者打造的演示文稿工具', { delay: 400 })

其主要原理是通過 afterStep 回調函數 將頁面上的輸入值,設置到 state 中,然後再使用 vue 中的 watch,監聽輸入值的變化,將 markdown 解析成 HTML 插入到頁面中。

import { ref, onMounted, watch } from 'vue'
import { parse } from '@slidev/parser'

...
watch([code, paused], () => {
  if (paused.value)
    return
  try {
    info.value = parse(code.value)
  }
  catch (e) {
  }
})
...

實現方式簡單,但卻讓用戶一目瞭然,讓用戶不用看文檔就可以學會使用 markdown 寫 PPT。

動態簡歷

之前在知乎上看到 @方應杭用 vue 寫了一個會動的簡歷 [3],也是運用了打字機效果,將輸入和輸出完美的展現在瀏覽器裏,若不瞭解其原理會覺得很高大上,但實現代碼卻很簡單,源碼在這裏 [4]

會動的簡歷

學以致用

我之前使用 MDX 寫了一個微信排版編輯器 MDX Editor[5],正好少了一個首頁,能否加上打字機效果呢?

mdx-editor

可自定義組件、樣式、生成二維碼、代碼 diff 高亮,一鍵拷貝到微信,可導出 markdown 和 PDF。 關於代碼和原理就就不貼了,大致和 Sildev 差不多,只不過我使用的是 react 來實現,代碼已經開源 [6],若對你有幫助, 可以點個 star,感謝您的支持!

以上就是本文全部內容,希望這篇文章對大家有所幫助,也可以參考我往期的文章或者在評論區交流你的想法和心得,歡迎一起探索前端。

[1]Sildev: https://cn.sli.dev/

[2]Sildev demo 源碼: https://github.com/slidevjs/docs-cn/blob/main/.vitepress/theme/components/demo/Demo.vue

[3] 會動的簡歷: https://zhuanlan.zhihu.com/p/25541520

[4] 會動的簡歷源碼: https://github.com/jirengu-inc/animating-resume

[5]mdx-editor: https://editor.runjs.cool/

[6]mdx-editor 源碼: https://github.com/maqi1520/mdx-editor

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/BjMiSV9jkCO-IvEHJ76NBA