よそ様が咲-Saki-祭り状態だというのに何をやっているのか…
まあ一本だけ記事書いたけれども。ホントは五本書く予定だったんだ(虚言癖)
あ!単行本買ってたのに、オビをよく見てなくて、まとめアンテナ様の記事リストで千里山編の始動を知りました。
どうにかして咲-Saki-愛を…咲-Saki-愛を示さなくては!

とか言ってそれほど示さないんですけどね。

煩悩の数だけ数牌がある

さて。
いつもの牌テクノロジーカテゴリの記事の例に漏れず、反響ゼロの雀牌カウンターですが。
前記事の仕様では表示する牌を一枚ごとにランダムに選ぶだけだったので、同じ牌が五枚とか、が二枚とか、表示されることもあり得ました。

それで、改良版を作りましたので、また自己満足的解説をします。

※スマホの方はこちらから御覧下さい→
「雀牌ビジターデモ:ver.2.1」というタイトルが出ない場合は、再読み込みすると改良版が表示されます。


改良版ではひとつの麻雀牌セットからランダムに選び、重複が出ないようにしました。

そのためにまずこういう変数を用意します。
var weight = [              //各牌の枚数のリスト
    4,4,4,4,3,4,4,4,4,1,        //萬子牌(最後は赤)
    4,4,4,4,2,4,4,4,4,2,        //筒子牌(最後は赤)
    4,4,4,4,3,4,4,4,4,1,        //索子牌(最後は赤)
    4,4,4,4,4,4,4            //字牌
];
var remain = sum(weight), len = weight.length, PN;
weight は各牌の枚数の排列です。
たとえばweight[0]にはの枚数「4」 をセットしています。
remain には weight の合計値を入れたので、初期値は136です。
PN は、確定した牌の番号を入れる変数です。

そして、前回の初期化ループの中に、以下の処理を追加しました。
if(!remain)PN = len;
136枚あった牌の残り枚数が0になっていたら、PN に len(=37) を代入します。
weight の排列長 37 に対して、牌は「ura」を加えて 38種類用意してあります。
だから[37]で最後の牌コード「ura」が参照されて、137枚目からは、すべて を表示するというわけです。
まあこのブログに同時に137人もの閲覧者が来られることはないので、この部分が機能する機会はないでしょう…
else{
  var tmp=0, pn = Math.floor((Math.random())*remain);
PNが牌種のインデックスであるのに対し、pnは残り枚数ベースの牌番号です。
残った牌の中から、ランダムに番号を選びます。
tmp は何枚目まで数えたか、を入れておく一時変数。
  for(var ii=0;ii<len;ii++){
      tmp += weight[ii];
      if((pn <= tmp) && (weight[ii]>0)){
          weight[ii]--, remain--, PN = ii;
          break;
    }
  }
}
tmp に weight[0],weight[1]…の値を順次加算して行き、その値が pn以上になったら weight[?]の値を調べます。
これが0であれば、その牌はもう使い切っているということなので、飛ばして次に行きます。
0でなければその瞬間の weight[?]が選ばれた牌になります。
その選ばれた牌の残り枚数と合計値を一減らし、この ? をPNに指定して、ループを抜けます。

以上の処理により、始めに書いた問題は解決しました。

とはいうものの

まあ137人どころか7人も来て下さっていれば歓喜してしまうわけですけれども。
想定はしておきたい。
ので、閲覧人数を取得した直後に、この一行を、謹んで書き加えました。
s>150&&(s=150);    //…(1)
普通の書き方だと
if(s>150)s=150;    //…(2)
でいいんですが、すみません。カッコつけました。
&& はプログラミング言語の論理演算子というやつで、「かつ」という意味、s には訪問中の人数が格納してあります。
前半の s>150 をまず評価し、成立していなければ以降は無視して false(偽)を返します。
一発目で false なら、以降がすべて true(真) だったとしても「かつ」としては既に false が確定しているため、評価する必要がないからです。
だから後半の s=150 という式は、s>150 が成立しない限りはスキップされて、結果的に(2)と同じ意味になるというわけです。

これで、どんなに 閲覧人数が多くても、牌は150枚までしか表示しない、という仕様の完成です。

ついでに

表示枠のCSSは
#visitors{
    margin: 0 auto;
    position:relative;
    overflow:hidden;
    width:180px;
    height:60px;
    background:#FAAC58;
    border:ridge 3px brown;
}
と、特に工夫もないただのボックスです。???「デザインもイマイチだよ!」
height(高さ)は始め90pxでしたが、ちょっとスカスカかなと思って60pxに変えました。
それと、前回触れたように、スクリプトの中でCSSを取得するようにしました。
    var $V = $('#visitors');
    var XX = $V.width(), YY = $V.height()+s, xx= 20, yy = 29;
    $V.height(YY);//高さ調節
そして、上記のように高さ YY に s を加えるようにしました。
10人が訪問中なら 60px + 10px = 70px のように。
牌の数に応じて縦に伸び縮みします。

そして…
ninja-counter-view2
これがテストで表示させた、s=150の最高状態の全牌や。
表示枠の高さが 60px + 150px = 210px になり、136枚すべての牌と、14枚の裏向きの牌(計150枚)が表示されている…はずです。

ちょっと横道 width() と height() の話

ところでこの jQueryのライブラリにある width() と height() は面白いメソッドで、
    var x = element.width();
    var y = element.height();
のように()の中を空にするとその要素の幅/高さを数値で返し、
    element.width(180);
    element.height(90);
のように()内に数値を指定すると、要素の幅/高さをその数値に変えることができます。 width も height も CSSプロパティなので cssメソッドを使うこともでき、同じことを
    var x = element.css('width');
    var y = element.css('height');
    element.css({width:'180px',height:'90px'});
として実現できます。
ただし、cssメソッドの方は指定値も返り値も文字列を使うので、状況に応じて使い分ける必要があります。
私は始めcssメソッドの方しか知らず、横幅を数値として求めるのに
    var x = parseInt(element.css('width').slice(0,-2));
みたいなややこしい書き方をしていました。
いやいくら何でもこんな簡単な要求をこんな面倒臭く書くのが普通なわけないじゃないかーと思ってぐぐったら解決したんですけど。

名無しの牌には jQuery

至ってシンプルな、麻雀牌表示領域のCSS
#visitors>div{
    position:absolute;
    width:20px;
    height:29px;
    cursor:default;
}
#visitors>div というのは visitors というIDを持つ要素(表示枠)の直下にあるすべての div要素、という意味で、この div要素自体には名前(ID)はありません。
個別に操作するには ID があった方がいいのですが、まとめてループ処理をしているだけなので、jQuery のオブジェクトを使って
  $('#visitors>div')[番号]
のように参照すれば十分なのですね。
これを普通の JavaScript でやろうとすると、
  document.getElementById('visitors').getElementsByTagName('div')[番号]
みたいになります。
長いのはいいとしても、これだと直下の div要素 だけでなく、孫要素以下の div もあればすべて取得してしまうので、ものすごく面倒なノードチェックを入れなくてはなりません。
そんなことをするくらいならいちいち ID を振っておいた方がマシ、となります。
それに比べて、'#visitors>div' のような CSSのセレクタの書き方で手軽にオブジェクトを操作できるのが jQuery の便利なところです。

span から div へ

さて、そもそもこの麻雀牌は麻雀漫画咲-Saki-のネタバレ記事を書くに当って使い始めた牌画です。
悠彗の和了 西 西 ロン
これはシノハユ31話から。
で、これを表示するのに span要素を使っているので、今回も上の div の中に、何も考えず span を入れていたんですが…

そもそも span にしたのはもともと参考にした絵文字スプライトのサイトのソースがそうなっていたからで、横に並べていくのに div だと float 指定したり解除したりがあるので適していない、という理由に気づいたのはかなり後でした。

でも今回のこれはふわふわ動いてるわけだし
うーん、span を入れ子にする必要なくね?
せっかく div要素があるんだから、これに牌画のクラスを与えればいい話じゃね?
ということを思いついてしまったので、やってみました。

CSS

CSSはほとんどいじりません。ただし…
#visitors>div{
    position:absolute;
}
width と height はそういえば牌個別のクラスにあって重複していたので、削除!
開発()初期にテキストを表示していた名残である cursor …お前まだいたのかよ削除!

と、不要っぽいのをどんどん削っていったら、たった一行になってしまいました(笑)。

JavaScript

で、牌画を乗せている div要素生成部分の、ここ。
var cls = sprite[PN];
var html = '<div><span class="ancoro tile_'+cls+'" title="咲さんかわいい!"></span></div>';
var $div = $(html);
これを、
var $div = $('<div class="ancoro tile_'+sprite[PN]+'" title="咲さんかわいい!"></div>');
こう。
span が無くなった分、ちょっぴり短くなりましたし、一時変数も無くしたら一行で書けました。

いかにも jQueryっぽく書くなら
var $div = $(
    '<div/>',
    {
        'class': 'tile_' + sprite[PN],
        'title': '咲さんかわいい!'
    }
);
こうですが、パフォーマンスは前者の方が上のようです。

これで雀牌ビジター改良の記はおしまい。