[Deep css 調査隊] もうz-indexに頼るのをやめた開発日誌

2022年8月3日

テクノロジー

eyecatch HTMLの画像表示をオブジェクト指向で考える、ユゲタです。 ブラウザゲームを作っていると、HTMLとCSSの仕様に悩まされることが数多くあります。 通常、ホームページを作る上ではあまりやらないようなことも、ゲーム画面内では細かくセットしたくなることもあるし、 かなり複雑な構造体をマルチブラウザで維持しなければならない場面も多数遭遇します。 今開発しているモノで、キャラクター構造をHTMLのDOM構造とCSSのみで動作させようとした時に、 ユーザー操作によって、インタラクティブなモーションをキャラクターにさせたいと設計して、画像の前後判定を親子構造を超えてもっと自在に出来ないものかと考えていた時に、 これまでz-indexのみに頼っていたのを、translate3dを使うと思いの外うまく行ったので、そのやり方を備忘録しておきたいと思います。

やりたいこと

今回はインタラクティブにアニメーションさせるために、アニメーション途中で画像の前後判定を入れ替えるという荒業を実現したいと思います。 しかも、まあまあディープなネスト構造状態のDOMで実現したいと思います。 サンプルHTMLを作りました。 <div id='sample'>root <div class='box a-1'>a-1 <div class='box a-2'>a-2 <div class='box a-3'>a-3 <div class='box a-4'>a-4</div> </div> </div> </div> </div> <style> .box{ display:block; position:absolute; top:20px; left:20px; width:100px; height:100px; padding:5px; color:white; border:1px solid black; } #sample *{ -webkit-box-sizing : border-box; -moz-box-sizing : border-box; -o-box-sizing : border-box; -ms-box-sizing : border-box; box-sizing : border-box; } #sample{ position:absolute; width:500px; height:300px; top:0px; left:10px; border:1px solid black; background-color:#ddd; } #sample .a-1{ width:150px; height:200px; top:50px; left:50%; transform:translateX(-50%); background-color:red; } #sample .a-2{ width:100px; height:50px; top:20px; left:-80px; background-color:blue; } #sample .a-3{ width:100px; height:50px; top:20px; left:20px; background-color:green; } #sample .a-4{ width:100px; height:50px; top:20px; left:-20px; background-color:orange; } </style>
root
a-1
a-2
a-3
a-4
この状態のままでは、DOMに書かれた順番になっていますが、ストレートなネストになっているのa-2をa-1の下に表示したい場合、 a-2のz-indexを-1にすると、良さそうに思えますが、いくつかの条件が整わないといけません。
【z-indexの条件】 ・上位要素がz-index:auto;(デフォルト値)にセットされている事 ・上要素でtransformプロパティを使っていない事
この2つの条件がなんとも悩ましくめんどくさい上、z-indexのマイナス値を多重階層で前後判定をコントロールするのは、もはやカオスです。 これを解決するために、どの要素にtransformがセットされていてもいい状態と、z-indexの値に依存しにくいパターンを模索していて、 translateZの値でコントロールすればできることにたどり着きました。

解決編

まずは出来上がりのサンプルを見てください。 <div id='sample2'>root <div class='box a-1'>a-1 <div class='box a-2'>a-2 <div class='box a-3'>a-3 <div class='box a-4'>a-4</div> </div> </div> </div> </div> <style> .box{ display:block; position:absolute; top:20px; left:20px; width:100px; height:100px; padding:5px; color:white; border:1px solid black; transform-style: preserve-3d; } #sample2 *{ -webkit-box-sizing : border-box; -moz-box-sizing : border-box; -o-box-sizing : border-box; -ms-box-sizing : border-box; box-sizing : border-box; } #sample2{ position:relative; width:500px; height:300px; border:1px solid black; background-color:#ddd; } #sample2 .a-1{ width:150px; height:200px; top:50px; left:50%; background-color:red; transform:translateX(-50%) translateZ(1px); } #sample2 .a-2{ width:100px; height:50px; top:20px; left:-80px; background-color:blue; transform:translateZ(-1px); } #sample2 .a-3{ width:100px; height:50px; top:20px; left:20px; background-color:green; transform:translateZ(3px); } #sample2 .a-4{ width:100px; height:50px; top:20px; left:-20px; background-color:orange; transform:translateZ(4px); } </style>
root
a-1
a-2
a-3
a-4
どうですか?a-2がa-1よりも下に表示されていると思います。 ポイントは2つほど覚えるだけでできます。 ポイント1 : 前後判定の対象となる要素全てに、transform-style: preserve-3d;をセットする。これによって、translateZの値が設定できるようになります。 ポイント2 : 対象要素に、表示順番をpx値で記入する。transform:translateZ(-1px);1px単位ではなく、3px単位ぐらいでグローバル値を計算すると、任意の値での前後値がセットできるようになりました。(ブラウザ毎に値の差があるようです)

最後に

いったいこういう事をやって、何がどうなるのかと言うと、 キャラクターが自分の鼻を手で触るアニメーションの後で、 頭の後ろに手をやるアニメーションを実装することが可能になります。 これはあくまで一例ですが、見た目の前後判定って、非常にHTMLではコントロールするのがめんどくさいけど、今回のようにtranslateZのみで操作できるのは、 これまでz-indexのみに頼ってきた事を考えると遥かに便利なわけです。 これにより、ゲーム開発での利用範囲が無限大になりました。 ムフフ。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ