JavaScriptで1文字ずつ表示するやつ
こういうの↓。タイプライター風というらしい。こんな感じで作ってみた。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript"> window.onload = function(){ typewriter('はたらけど\nはたらけど猶わが生活楽にならざり\nぢつと手を見る', 'main'); } //************************************************** // 文字列を1文字ずつ表示 // text : 表示する文字列 // id : 対象要素 //************************************************** function typewriter(text, id){ var texts = text.split('\n'); var target = document.getElementById(id); var index = 0; var i = 0; var intervalID = setInterval(type, 100); function type(){ if(i < texts.length && index < texts[i].length){ target.insertAdjacentHTML('beforeend', texts[i].charAt(index++)); } else if(index >= texts[i].length) { i++; if(i == texts.length){ // 文字列を表示し終えたらsetIntervalを無効にして終了 clearTimeout(intervalID); } else { // 次の行へ target.insertAdjacentHTML('beforeend', '<br>'); index = 0; } } } } </script> </head> <body> <div id="main"></div> </body> </html>
type関数を作成し1文字表示したり改行したり。そしてsetIntervalを利用して待ち時間100msごとにtype関数を繰り返し呼び出すことで1文字ずつ表示する処理を実現。
しかしおそらく、setIntervalではなくPromiseというのを利用するのが今時の書き方?存在自体初めて知ったので、きちんと調べて書き換えてみたいと思う。
以下、失敗の記録。関数部分だけ記載。
◆初版
function typewriter(text, id){ var target = document.getElementById(id); var index = 0; var write = ''; var intervalID = setInterval(type, 100); function type(){ if(index < text.length){ write += text.charAt(index++); target.innerHTML = write; } else if(index >= text.length) { // 文字列を表示し終えたらsetIntervalを無効にして終了 clearTimeout(intervalID); } } }
改行されないんですけどぉ!
HTMLとして表示するから、改行は<br>にしてあげないと駄目だった。
◆第2版
function typewriter(text, id){ text = text.replace(/\n/g, '<br>'); var target = document.getElementById(id); var index = 0; var write = ''; var intervalID = setInterval(type, 100); function type(){ if(index < text.length){ write += text.charAt(index++); target.innerHTML = write; } else if(index >= text.length) { // 文字列を表示し終えたらsetIntervalを無効にして終了 clearTimeout(intervalID); } } }
\nを<br>に置換。
replaceメソッドは第1引数を正規表現で書いてあげると全置換してくれるようになる。 /置換元/g という書き方。詳しくは『グローバルマッチ』で検索!
…で、一瞬<が見えるのがとても気になる。
◆第3版
function typewriter(text, id){ if(text.match('\n')){ var texts = text.split('\n'); for(var i = 0 ; i < texts.length; i++){ typewriter(texts[i], id); document.getElementById(id).insertAdjacentHTML('beforeend', '<br>'); } return; } var target = document.getElementById(id); var index = 0; var intervalID = setInterval(type, 100); function type(){ if(index < text.length){ target.insertAdjacentHTML('beforeend', text.charAt(index++)); } else if(index >= text.length) { // 文字列を表示し終えたらsetIntervalを無効にして終了 clearTimeout(intervalID); } } }
改行ごとに区切って文字列を表示すれば良いんでしょ、という発想。1行分表示したら改行して次の処理へ。
今まではinnnerHTMLだったけれどinsertAdjacentHTMLに変更。innnerHTMLでは都度全文更新している状態だったが、insertAdjacentHTMLにすることで既存の文章+追加する文字という状態になった。こんな書き方もあるんだ…。
実行結果は愉快なことに。一番最初に改行され、まず動くtypewriter(texts[0], id);が待ち時間の間にtypewriter(texts[1], id);が動き、待ち時間の間にtypewriter(texts[2], id);が動き…ということみたい。これが非同期処理というやつかぁ。
で、修正したのが冒頭のソース。
setIntervalでループしているので、呼び出した先のtype関数でもろもろ制御することに。
あとは、第3版では改行有無で処理分けていたけれど分ける必要なかったよね、とか細々修正。
ひとまず完成したから満足!