はじめに
ニコニコ動画風の流れるコメントをJavaScriptで作ってみようという企画です。
かなり難しいので、作れる箇所から徐々に制作していきます。完成するかは未定。
HTML
HTMLは単純です。
スクリーン(div#screen)内にコメント(span.comment)を追加していきます。
スクリーン内には動画(video#video)も用意します
※コメントタグは動画タグの前に追加していきます
動画タグの後に追加する場合は #video に position:absolute が必要です
スクリーンのCSS
スクリーンのCSSです
#screen{ background-color: #000; width: 640px; height: 360px; overflow: hidden; white-space : nowrap; cursor: default; -ms-user-select: none; -moz-user-select: none; -webkit-user-select: none; user-select: none; position: relative; }
スクリーンからはみ出た文字を隠すため overflow:hidden を設定
上記を有効にするには position:relative も必要
文字が勝手に折り返らないように white-space:nowrap を指定
あとは文字を選択できないようにしたり、カーソルも変更
スクリーンのサイズは固定しておきます(640×360)。HD風に縦横比は1.777
コメントのCSS
コメントのCSSです
.comment{ position: absolute; font-size: 24px; line-height: 1; z-index: 2; color: #fff; text-shadow: -1px -1px #000, 1px -1px #000, -1px 1px #000, 1px 1px #000; }
文字を動かすために position:absolute を指定
文字色が背景色と被った時のために text-shadow で縁取りする (白文字に黒の縁取り)
念のために z-index も指定
文字を動かす
文字を動かす方法はいろいろありますが、CSSのtransformを利用するとよさそうです
CSSに下記を追加します
.comment{ animation-name: lane0; animation-timing-function: linear; animation-duration: 8s; animation-fill-mode: forwards; } @keyframes lane0 { from{ transform: translate(640px, 0); } to{ transform: translate(-1280px, 0); } }
CSSアニメーションを利用します。
動作関数は linear を指定するとニコニコ風になります
transform:translate(X, Y) で 要素を移動させることができます
とりあえず 右から左へとコメントが流れるようにします。(※Y軸は後回し)
from{} は アニメーションの開始に関する指定
640pxとはスクリーンの右端地点です(=スクリーンの横幅)。
ここからコメントの左端が動き始めます
to{} は アニメーションの終了に関する指定
どこまでコメントを移動させるべきかですが、スクリーン外まで動かします。
0pxがスクリーン左端地点なので、-1280pxはスクリーン2つ分左までになります
最大文字数を考慮しています。
from から to までは 8秒 かけて移動します。animation-duration:8s
アニメーション終了後は、その場に留まるように指定します。animation-fill-mode:forwards
画面外に流れたコメントを消す
ここまでで文字を流せる所まできましたが、これではコメントタグが大量に増えてしまいます。なのでコメントが完全にスクリーン外に出たらコメントを消します
スクリーン内の要素を監視
videoオブジェクトは動画再生中に timeupdate イベントを 3~4回/秒 発行します。
そのタイミングでスクリーン内のコメント要素を検索して、画面外なら消します
removeChild()で要素を削除できますが、ループは逆順に回す必要がある
for(var i = comments.length-1; i >= 0; i--){}
要素の位置は getBoundingClientRect() で取得できます。 (絶対位置)
「コメントの右端 < スクリーンの左端」なら画面外です
文字の重なり防止
ここまでのコードを動かしてみると、同時にコメントを流すとコメントが重なってしまいます。コメントが重ならないように、何らかの工夫が必要になりますが、難しい所です。
・スクリーンを縦にn分割する。 (スクリーン縦サイズ ÷ フォントサイズ が目安)
・分割したものはレーンと呼び、レーン毎にコメントを流す
・コメントを放出したらレーン[n]を false に
・レーン[n]が false のレーンにはコメントを放出しない
・スクリーン内にある全コメントタグを監視して、コメントの右端がスクリーン右端より外ならば、そのレーン[n]は false に(それ以外はtrueに)
これでコメントが重なることはないと思います。
今回は12レーン用意します。
12レーン×25pxで300px。下60pxは使わないことにします
12レーン分のCSSを用意しましょう。下記のCSSを追加
@keyframes lane1 { from{ transform: translate(640px, 25px); } to{ transform: translate(-1280px, 25px); } } @keyframes lane2 { from{ transform: translate(640px, 50px); } to{ transform: translate(-1280px, 50px); } } /* 以下lane11まで省略 */
@keyframes をCSS直書きでなくJavaScriptから追加したいなら次のように記述します
for(var i = 0; i < 12; i++){ var keyframe = ""; keyframe += "@keyframes lane" + i + "{\n"; keyframe += "from{transform:translate(640px," + i*25 + "px);}\n"; keyframe += "to{transform:translate(-1280px," + i*25 + "px);}\n}\n"; document.styleSheets[0].insertRule(keyframe, 0); }
コメントをどのレーンに流したかは コメントタグの data-lane 属性に記録しておく
コメントのフォーマット
コメントはサーバからJSON形式で受け取ることにします
フォーマットはこんな感じで。(投稿時間でソートしておく)
[ ["コメント1", 動画時間, 投稿時間(UNIXタイム)], ["コメント2", 動画時間, 投稿時間(UNIXタイム)] ]
ただ、これでは使いにくい気がするので動画時間毎に整理します
例えば100秒の動画としたら配列の要素数を101にして、秒毎に整理します
配列[動画秒数] でアクセスできるので使いやすい気がする
[ [//0秒台のコメント ["コメント1", 動画時間, 投稿時間(UNIXタイム)], ["コメント2", 動画時間, 投稿時間(UNIXタイム)] ], [//1秒台のコメント ["コメント3", 動画時間, 投稿時間(UNIXタイム)], ["コメント4", 動画時間, 投稿時間(UNIXタイム)] ] ]
コメントを停止する
動画が停止した時にコメントも停止するようにします。
動画が停止した時は pause イベントが発行します
コメントを停止するには、コメントタグのスタイルに animation-play-state: paused;
をセットします。
コメントを再生するには、play イベントが発行した時に running をセットします
コメントをクリアする
シークした時、動画が最後まで到達した時はコメントを全て削除する
seeking と ended イベントです
コメントを流す
流すタイミング
コメントを流すのは1秒毎にします。
現在の動画の再生時間は video.currentTime から取得できます
timeupdate イベントが発行する毎に 現在の時間を調べて、前回の秒と違う時にコメントを流せばよいでしょう
動画が停止中にはコメントを流さないようにします。video.pausedの真偽値を調べる
これをサボると、ポーズした時に稀にコメントが流れてしまいます
※飽きてきたので、説明はここまでとします
詳しくは完成品のソースをご覧ください