
電脳麻将 では牌を画像として表示していますが、副露メンツ内の横向きとなっている牌はどのように実現しているのでしょうか*1。
例えば ![]()
![]()
の場合、HTMLは以下となっています。
<span class="mianzi">
<span class="rotate">
<img class="pai" data-pai="m3" src="img/m3.png">
</span>
<img class="pai" data-pai="m1" src="img/m1.png">
<img class="pai" data-pai="m2" src="img/m2.png">
</span>
メンツ全体を class="mianzi" の要素で囲い、その中に表示順序にしたがい牌の画像を並べます。 ただし、横向きにする牌は class="rotate" の要素で囲んでいます。
これを以下のようなCSSで必要な部分を回転させます。
.mianzi .pai {
display: inline-block;
height: 48px;
width: 36px;
}
.mianzi .rotate {
display: inline-block;
white-space: nowrap;
text-align: left;
width: 48px;
transform-origin: 0% 0%;
transform: rotate(270deg) translate(- 48px, 0px);
}
回転させる部分の幅を牌の高さ(width: 48px)に合わせ、起点を左上(transform-origin: 0 0)として反時計回りに90°回転(transform: rotate(270deg))させて、回転後の座標軸で左へ牌の高さ(transform: translate(- 48px, 0px))だけ移動(つまり下に移動)させています。
電脳麻将では、Stylus の Mixin を使用してCSSを部品化しています。 面子に関しては下記の部品があります。
pai-width($height)
$height / 4 * 3
pai-size($height)
display: inline-block
height: $height
width: pai-width($height)
mianzi-size($height)
display: inline-block
.pai
pai-size: $height
.rotate
display: inline-block
white-space: nowrap
text-align: left
width: $height
transform-origin: 0% 0%
transform: rotate(270deg) translate(- $height, 0px)
MixinはあたかもCSSのプロパティのように動作します。 mianzi-size: 48px とすると先に挙げたCSSがすべて設定されます。
面子を表すDOMノード群を生成するのが @kobalab/majiang-ui の内部関数 mianzi です。
/*
* Majiang.UI.mianzi
*/
"use strict";
const $ = require('jquery');
const label = require('./label')('mianzi');
module.exports = function(pai) {
function get_label(p) {
return pai(p).attr('alt')
? [ 'alt', pai(p).attr('alt') ]
: [ 'aria-label', pai(p).attr('aria-label') ];
}
return function(m) {
let mianzi = $('<span>').addClass('mianzi');
let s = m[0];
if (m.replace(/0/g,'5').match(/^[mpsz](\d)\1\1\1$/)) {
// 暗槓
let nn = m.match(/\d/g);
mianzi.attr('aria-label', label.angang)
.append(pai('_').attr(...get_label(s+nn[0])))
.append(pai(s+nn[2]))
.append(pai(s+nn[3]))
.append(pai('_').attr(...get_label(s+nn[1])));
}
else if (m.replace(/0/g,'5').match(/^[mpsz](\d)\1\1/)) {
// 刻子・大明槓・加槓
let gang = m.match(/[\+\=\-]\d$/);
let d = m.match(/[\+\=\-]/);
let nn = m.match(/\d/g);
let pai_s = pai(s+nn[0]);
let pai_e = (! gang && nn.length == 4)
? nn.slice(1, 3).map(n=>pai(s+n))
: nn.slice(1, 2).map(n=>pai(s+n));
let pai_r = $('<span>').addClass('rotate')
.append(gang ? nn.slice(-2).map(n=>pai(s+n))
: nn.slice(-1).map(n=>pai(s+n)));
mianzi.attr('aria-label', label[d]
+ ( gang ? label.gang
: nn.length == 4 ? label.minggang
: label.peng ));
if (d == '+') mianzi.append(pai_s).append(pai_e).append(pai_r);
if (d == '=') mianzi.append(pai_s).append(pai_r).append(pai_e);
if (d == '-') mianzi.append(pai_r).append(pai_s).append(pai_e);
}
else { // 順子
let nn = m.match(/\d(?=\-)/).concat(m.match(/\d(?!\-)/g));
mianzi.attr('aria-label', label.chi)
.append($('<span>').addClass('rotate').append(pai(s+nn[0])))
.append(pai(s+nn[1]))
.append(pai(s+nn[2]));
}
return mianzi;
}
}
mianzi() は関数を返す関数として実装しています。 電脳麻将UI 〜 牌 で紹介した「牌を生成する関数」を引数に mianzi() を呼び出すと「面子を生成する関数」を返します。
返された関数は、引数で指定された 面子 を表すDOMノードを以下の要領で生成しています。
以下で実際の表示を確認できます。