使用 CSS 和 JS 呈現打字機效果

使用 CSS 和 JS 呈現打字機效果

這篇主要是說明如何運用幾個 CSS 和 JS 觀念來做出打字機的效果。

話不多說,先看 DEMO:
DEMO

主要觀念

  • CSS
    • keyframes
    • animation 設定
  • JS
    • setInterval()
    • contains()
    • if, else

程式碼說明

HTML

先從 HTML 結構來說:
第一層 div 是為了設定鼠標的活動空間是和字體寬度相等,因此會設定為 display: inline-block
第二層 div (#typewrite-animated) 則是打字機動畫效果的設定層
再往內是打字機文字顯示的地方 (#typewrite-text)

1
2
3
4
5
<div class="d-inline-block">
<div id="typewrite-animated">
<span id="typewrite-text" class="d-inline-block"></span>
</div>
</div>

CSS

接著來看 CSS 樣式設定的部分
如同上面 HTML 區塊提到的,為了讓寬度適應內容,會設定 display: inline-block。因此第一個樣式就是:

1
2
3
.d-inline-block {
display: inline-block;
}

接著進入重點的鼠標以及打字機效果的說明。
我們把主要的動畫效果寫到 .typewriter span,而 typewriter 就是要加到 #typewrite-animated 並動態被切換的 class

1
2
3
4
5
6
7
8
9
.typewriter span {
font-size: 72px;
overflow: hidden;
border-right: .3rem solid $dark;
white-space: nowrap;
animation:
typing 0.6s steps(3, end),
blink-caret .5s step-end infinite;
}

  • font-size: 設定字體大小
  • overflow: hidden: 隱藏文字顯示
  • border-right: 設定鼠標寬度
  • white-space: nowrap: 讓文字不換行
  • animation: 設定動畫

animation

這邊要特別說明動畫的部分,有加上兩個值。
第一個是 typing 0.6s steps(3, end)
typing 0.6s steps(3, end) 中的第一個參數 typing 是說明動畫名稱,會對應到 keyframe 的設定

1
2
3
4
@keyframes typing {
from { width: 0 }
to { width: 100% }
}

也就是外部的寬度由 0 到 100%,而 0.6s 則是指這個動畫過程的時間,第三個參數則是指 animation-timing-function。animation-timing-function 這個屬性是用來控制動畫播放的速度與加速度,常見的值有 linear、ease-in、ease-out 等等,而這邊使用的 steps 也是其中的值,這邊就來介紹一下 steps 吧!

steps()

steps 中可以帶兩個參數,第一個是數字,代表的是要將動畫切為幾格,第二個 position 是指動畫從斷點頭或是斷點尾開始。

從 W3C 的圖來看是這樣:

圖片來自 W3C

實際從範例可以更清楚地理解,請打開以下範例:
https://codepen.io/carlos411/pen/wRKRej

範例來自 Carlos-Studio 設計好網站

到這邊就可以了解 steps() 的使用,可以讓文字變成一個一個出現。而範例呈現的文字數有 3 個,所以 steps 設定是 3。以此類推,若有五個字就 steps 就設定為 5。

而第二個值 blink-caret .5s step-end infinite; 用法也如同上面提到的,blink-caret 是動畫名稱,對應的 keyframes 是設定鼠標的閃爍效果,由透明到黑色。

1
2
3
4
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: $dark }
}

同樣使用 step-end 讓鼠標有閃動的感覺,最後參數 infinite 讓動畫可以持續的重複播放

JS

最後說明一下 JS 的部分:
主要是透過 setInterval() 來做每一次的切換變化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 透過 arr 陣列儲存要變化的文字內容
const arr = ['寫程式', '做設計', '找工作'];
// 用 count 計算次數,後面會用來計算目前要顯示的文字內容是哪一個
let count = 0;

// 用 setInterval 做每一次的切換變化
setInterval( function() {
count += 1;
// 設定動畫名稱並取得元素
const typewriteAni = document.getElementById('typewrite-animated');
const typewriteText = document.getElementById('typewrite-text');

// 判斷 #typewrite-animated 的 class 中是否含有 'typewriter' 這個 class
if (typewriteAni.classList.contains('typewriter')) {
// 有的話就移除
typewriteAni.classList.remove('typewriter');
} else {
// 沒有的話就加上
typewriteAni.classList.add('typewriter');
// 監聽元素 animationend 事件,動畫執行完 800 毫秒後清掉文字與 'typewriter' 這個 class
typewriteAni.addEventListener('animationend', function() {
setTimeout(function() {
typewriteText.textContent = '';
typewriteAni.classList.remove('typewriter');
}, 800)
})
}
// 用 count%3 來計算目前要取得的 arr 位置是第幾個(arr 中有 3 筆就用 %3,五筆就 %5)
typewriteText.textContent = arr[count%3];
}, 2500);

JS 補給站:

animationend event

animationend 事件,就如同 click 事件一樣,可以透過 addEventListener 來對元素做監聽。animationend 是指在 CSS 動畫執行完後再執行後面函式設定的內容

contains()

contains 方法可以用在 DOMTokenList,而元素的 classList(ele.classList)回傳的正是一個 DOMTokenList 物件,所以這邊要確認是否有某個 class 就需要使用 contains。

有一個與 contains 很像的方法,也就是 Array 的 includes,要注意這邊若要使用 includes,要先將 DOMTokenList 轉為 Array。
例如:[...typewriteAni.classList].includes('typewriter')

參考資料

animation-timing-function 的 steps 函式
Carlos-Studio 設計好網站

評論

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×