【JavaScriptで】ニコ動の上でネギを振らせてみた【ふるみっく】

むしゃくしゃして(初投稿を)やった。(出来の悪さに対する)反省はしていない。

魔法の言葉:

javascript:(function(){var d=document,ti=0,tj=1,a=new Array(),ta="<span style='display:none'>",tb="</span>",tc=tb%2Bta,mt=d.createElement("span");mt.style.fontFamily = "monospace";mt.innerHTML=ta%2B"\"%2Btc%2B"("%2Btc%2B"\"%2Btc%2B"_"%2Btb%2B"( ゜△ ゜ )";function f1(p,n){if(n.firstChild)f1(n,n.firstChild);if(n.nextSibling)f1(p,n.nextSibling);if(n.nodeName=="#text"){t=n.nodeValue.replace(/初音|はちゅね|ミク|ネギ|ねぎ|、/g,"。").split("。");p.insertBefore(d.createTextNode(t.shift()),n);while(t.length){p.insertBefore(a[a.push(mt.cloneNode(true))-1],n);p.insertBefore(d.createTextNode(t.shift()),n);}p.removeChild(n);}};f1(null,d);function fs(){ti=tj;if(%2B%2Btj==4)tj=0;for(i=0;i<a.length;i%2B%2B){a[i].childNodes[ti].style.display="none";a[i].childNodes[tj].style.display="inline";}};fs();setInterval(fs, 300);})();

魔法の言葉(改造版):

javascript:(function(){var d=document,ti=0,tj=1,a=new Array(),ta="<span style='display:none'>",tb="</span>",tc=tb%2Bta,mt=d.createElement("span");mt.style.fontFamily="monospace";mt.innerHTML=ta%2B"\"%2Btc%2B"("%2Btc%2B"\"%2Btc%2B"_"%2Btb%2B"( ゜△ ゜ )";function fr(p,n){if(n.firstChild)fr(n,n.firstChild);if(n.nextSibling)fr(p,n.nextSibling);if(n.nodeName=="#text"){t=n.nodeValue.replace(/[ \t\r\n]%2B/g,"").length/10;while(t-->0)p.insertBefore(a[a.push(mt.cloneNode(true))-1],n);p.removeChild(n);}};fr(null,d);function fs(){ti=tj;if(%2B%2Btj==4)tj=0;for(i=0;i<a.length;i%2B%2B){a[i].childNodes[ti].style.display="none";a[i].childNodes[tj].style.display="inline";}};fs();setInterval(fs,300);})();

【JavaScriptで】ニコ動の上でネギを振らせてみた【ふるみっく】 - ニコニコ動画

ちなみに、この魔法の言葉はブックマークレットとして使えます。たぶん。(Firefox2は大丈夫だった。)

というか、この程度の動画仕上げるのに5時間以上かかるってどうよ? orz

以下、舞台裏。

あれ? 初投稿は電子工作ネタじゃなかったの?

始めてのニコニコ動画の投稿は、以前ネタにした「はちゅね小型化戦争」に関連した電子工作ネタでやろうかと思って、色々と準備はしてたんですが、今のところちょっとした事情で中断しちゃってます。

このJavaScriptのネタに関しては、先週JavaScriptでのネギ振りを見た時点で「ブックマークレットで出来るよなぁ……」と思いはしたのですが、「まー、誰かやってくれるよね。つか、私、JavaScriptでまともなプログラム書いたことないし」とか思って放置してました。

で、先週は仕事の関係が忙しくて、殆ど余暇時間がとれなかったので、あんまりニコニコ技術部もウォッチしてなかったんですが、先週末に見てみると、このネタを使った動画が上がってない事に気付き、ついかっとなってJavaScriptプログラミングを開始していた……と。

JavaScriptってなかなか良い言語だね

JavaScriptは、IE4とかの時代にステータスバーを書き換えて遊んでいたぐらいで*1、まともにつかったことは殆どありません。

で、リファレンスを引き引き、なんとか書いてみたのが前述のスクリプトです。

圧縮された状態では読みにくいと思いますので、以下に展開して書きます。

javascript:(function(){
  var d=document,ti=0,tj=1,a=new Array(),
      ta="<span style='display:none'>",tb="</span>",tc=tb+ta,mt=d.createElement("span");
  mt.style.fontFamily = "monospace";
  mt.innerHTML=ta+"\"+tc+"("+tc+"\"+tc+"_"+tb+"( ゜△ ゜ )";
  function f1(p,n){
    if(n.firstChild) f1(n,n.firstChild);
    if(n.nextSibling) f1(p,n.nextSibling);
    if(n.nodeName=="#text") {
      t=n.nodeValue.replace(/初音|はちゅね|ミク|ネギ|ねぎ|、/g,"。").split("。");
      p.insertBefore(d.createTextNode(t.shift()),n);
      while(t.length) {
        p.insertBefore(a[a.push(mt.cloneNode(true))-1],n);
        p.insertBefore(d.createTextNode(t.shift()),n);
      }
      p.removeChild(n);
    }
  };
  f1(null,d);
  function fs() {
    ti=tj;
    if(++tj==4) tj=0;
    for(i=0;i<a.length;i++) {
      a[i].childNodes[ti].style.display="none";
      a[i].childNodes[tj].style.display="inline";
    }
  };
  fs();
  setInterval(fs, 300);
})();

ざっと解説します。

f1関数はHTMLの中を巡回してテキストノードを探し、そのテキストの中から"初音","はちゅね","ミク","ネギ","ねぎ","、","。"のいずれかを見つけたら、それを変数mtとして作成しておいたHTMLエレメントと置換して、配列aに配置したHTMLエレメントを保存します。mtの中身はネギの振り全パターンをhidden状態で含んでいるspan要素です。関数fsはインターバルタイマーで300ミリ秒おきに呼び出される関数で、配列a内の各要素から、現在表示中のパターンをhiddenにして、次のパターンを表示するようにします。

ふるみっく版の方は、関数f1()内のテキストノードを見付けた時の処理が以下のようになっている以外は同じものです。

    if(n.nodeName=="#text") {
      t=n.nodeValue.replace(/[ \t\r\n]+/g,"").length/10;
      while(t-->0) p.insertBefore(a[a.push(mt.cloneNode(true))-1],n);
      p.removeChild(n);
    }

こちらは、テキストノードの長さを数えて、その10分の1程度の数のmtに置き換えるものになります。実はこっちの方が単純なコードなんですよね。


できるだけシンプルで短かく仕上げたつもりなんですが、500文字をゆうに越えるコード量になってしまったので、IE6では動かないそうです(参考にしたサイト: http://www.teria.com/~koseki/memo/bookmarklets/tips.html)。 わたしの技術ではこのぐらいが限界っぽいので、これを見た誰かが完成させてくれることを期待して、私はギブアップしますw

コーディング等でハマった所

不慣れなJavaScriptコーディングで一通り罠を踏みまくったのでメモ。

  • DOMのテキストノードの空白文字列の扱いにIEFirefoxで違いがある
    • Firefoxではタグ間のスペースや改行もテキストノードになる。これもミクに置き換えてしまうと表示が激しく壊れる
    • あと、スクリプトを使ってテキストノードを複数個連続で配置すると、IEは配置された瞬間に1つのノードにまとめなおしてしまうっぽい。
  • IEのString.splitに正規表現を使ったらIEでは空要素が削除されて寂しいことに。
    • これも空白文字列の扱いの違いのせいか?
    • 今回は replace + split(文字列版) で逃げた
  • コード中のプラス記号は、URLエンコードではスペースを表す特別な記号なので、エスケープしておくべき("+"を"%2B"に置換)。
    • FirefoxだとそのままでもOKなんだが、IEだとスペースとして扱われてしまう。
    • 今回のコード中には無いが、"%"記号も同様の問題あり
    • その他の記号等はURLエンコードしてなくても大抵のブラウザでは問題無い。
  • 一方、日本語等の文字は、あえてそのままにしておく必要がある

動画作成でハマった所

動画の作成も始めてのことだったので、あちこちハマりまくり。やっぱりLinuxはこの用途に関しては、まだまだ全然洗練されてませんね。

詳しい話は長くなるので、後日別にまとめましょ。

SMILEVIDEO でハマった所

ただ1点。

動画の説明文には文字数制限があるので、2つのソースコードをまるごと貼ることができなかった orz

というか、もうちょっとコードが長かったら1つ貼るのも出来なかったかも……。

動画をうpした感想

めんどくせぇw

つか、もっとスマートに出来るようにならなきゃあかんな。

*1:最近はブラウザのポリシーで、ステータスバー書き換えは出来なくなってたりするんだよね