わんコメのCUIタイピング風テーマ作ってみた
わんコメ(YouTube Live などで使うコメントビューワー)用にタイピングアニメーションっぽいテーマを作ったので、ざっくり解説とおすそ分けです。
↓テンプレートは、ここから雑に落として使ってみてください
わんコメ
この記事は VTuber・バーチャルプログラマ・技術者 Advent Calendar 2023 の10日目の記事です。
動作デモ
やったこと
- チャンネル名とコメント本文の間に
:~$
または:~#
の文字列を挿入 - タイピングアニメーション
- 文字を1文字ずつ順に表示
- 文字の右端にカーソル的な縦棒を表示
:~$
の挿入
index.html
に直接挿入したい文字列を入れる
{{comment.data.displayName.trim()}}:~<span class="normal-user">$</span><span class="root-user">#</span>
$
と#
の切り替えは、JavaScript じゃなくて CSS でやる
実は CSS で、特定の属性値を持つ要素にだけスタイルを当てる、みたいな条件分けができる
わんコメでは、チャンネル主のコメントはdata-owner="true"
、それ以外だとdata-owner="false"
が設定される
div[data-owner="true"] .normal-user {
display: none;
}
div[data-owner="false"] .root-user {
display: none;
}
CSS の属性セレクター
タイピングアニメーション
1文字毎に CSS アニメーションを付与
各文字毎に、スイッチ的に表れるアニメーションを付与
これは1文字毎にループを回したいので JavaScript でやる
mounted()
の中でコメント取得時の callback を定義している場所があるので、そこの中にコメントの編集処理を挿入する(今回ドキュメント読まずに勘で編集したので細かいことは調べてください。すいません)
OneSDK.subscribe({
action: 'comments',
callback: (comments) => {
const newCache = new Map()
comments.forEach(comment => {
const index = cache.get(comment.data.id)
if (isNaN(index)) {
comment.commentIndex = commentIndex
newCache.set(comment.data.id, commentIndex)
++commentIndex
/* ↓ ここ ↓ */
comment.data.comment = attachTypingAnim(comment.data.comment)
} else {
comment.commentIndex = index
newCache.set(comment.data.id, index)
}
})
cache = newCache
this.comments = comments
}
})
comment.data.comment
にコメントテキストが入ってるのでそれを編集する
1文字1文字を <div class='char'></div>
で囲んでアニメーションをあてる
const htmlToTypeChar = (html, index) => {
return `<div class='char'>${html}</div>`
}
const attachTypingAnim = (comment) => {
comment = Array.prototype.map.call(comment, htmlToTypeChar).join('')
return comment;
}
CSS のアニメーション
アニメーションが再生された瞬間に瞬時に、透明度が0から1に変化する
.char{
opacity: 0;
animation: type 0ms step-start forwards;
}
@keyframes type {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
animation-delay
プロパティで遅らせる
アニメーションの開始時間を 文字毎にアニメーションを当てただけだと、全部の文字が同じタイミングで一斉に表示されてタイピングにならない
animation-delay
でアニメーションの開始を少しずつ遅らせて、それっぽくする(1文字目は0.1秒後表示、2文字目は0.2秒後表示...)
さっきの div 要素に直接プロパティを書く
const TYPING_INTERVAL = 100
const TYPING_DELAY = 200
const htmlToTypeChar = (html, index) => {
const delayMiliSecond = index * TYPING_INTERVAL+TYPING_DELAY
return `<div class='char' style='animation-delay:${delayMiliSecond}ms'>${html}</div>`
}
border
プロパティでそれっぽくする
カーソルっぽいバーは 縦棒の表示は border
プロパティでやる
文字の表示とは逆に、アニメーション終了時には消えてないといけない(文字と文字の間に毎回縦棒が入る)ので、border
プロパティについてもアニメーションを入れる
.char{
border-right: 0.1em solid var(--cui-main-color);
animation: bar 100ms step-end forwards;
}
@keyframes bar {
from {
border-right: 0.1em solid var(--cui-main-color);
}
to {
border: none;
}
}
絵文字に対応する
わんコメは入力された絵文字を html の img タグで流してくる
(反対にコメ欄に入力された<>
はエスケープされる)
さしあたって、img タグだけを1文字単位で扱えればいいので
- img タグをなんか特殊な文字で置き換え
- アニメーション付与の処理
- 特殊な文字を img タグに戻す
で、対応する
img タグに対応したのは↓
コメント欄に\t
が入ることはないので、img タグをいったん\t
に置き換えて最後に戻す
const attachTypingAnim = (comment) => {
const imgReg = /<img [^<>]+>/g
const imgTags = [...comment.matchAll(imgReg)]
const placeHolderToken = '\t'
comment = comment.replaceAll(imgReg, placeHolderToken)
comment = Array.prototype.map.call(comment, htmlToTypeChar).join('')
imgTags.forEach((value) => {
comment = comment.replace(placeHolderToken, value[0])
})
return comment;
}
animation-delay
を大量に使うとアニメーションがなんかバグる
最後に、長文コメントを入れると、縦棒が消えるアニメーションが実行されたりされなかったりするバグが出た
chrome だと起こって firefox だと起こらない
ググるとそれっぽい記事は出るけども、イマイチ原因が分からない
色々いじった結果、縦棒のアニメーションに中間点を入れるとなぜか解決した
これに関しては本当に謎なので、分かる人教えてください🙇
@keyframes bar {
from {
border-right: 0.1em solid var(--cui-main-color);
}
80% {
border: none;
}
to {
border: none;
}
}
Discussion