【CSS】transform-style:preserve-3dを指定した要素を半透明にできない?原因と対策

no_?thumbnail no_?thumbnail

直感的には理解できない不具合。

CSSで3D表現をするのは結構なモノ好きと見えて、日本語の情報源が一切なかったので備忘録として。

実際にあった失敗例

半透明のサイコロを作りたいとします。たとえば以下の例。

See the Pen 要素を半透明にする – by 吉川音依 (@tyvmyosm-the-scripter) on CodePen.

色も画像も透過できています。これはdiv要素のbackgroundを透過色にしているだけで、要素自他が半透明なわけではありません。なのでborderや上に乗っているp文字は不透明度100%です。

しかし実際には要素丸ごと半透明にしたいという場合が多いでしょう。サイコロの面を構成するdiv要素にそれぞれopacity:0.5;を設定するとどうなるでしょうか。

See the Pen transform-style: preserve-3d; – 要素を半透明にする② by 吉川音依 (@tyvmyosm-the-scripter) on CodePen.

こんな具合で半透明にできます。今度は要素全体が半透明なので文字も透けています。

では、その上の階層にあるdiv要素にopacity:0.5;を適用してみます。普通に考えればこれで子孫要素全てが丸ごと透過されるはずです。

See the Pen transform-style: preserve-3d; – 要素を半透明にする③(失敗例) by 吉川音依 (@tyvmyosm-the-scripter) on CodePen.

ところが、preserve-3dの世界ではどうもうまくいかないようです。透過こそされたものの、サイコロが平面になってしまいました。

どうしてこうなってしまうのか

opacity を1未満に設定した要素は transform-style が自動で flat に上書きされてしまうことが直接的な原因です。

このサイレントオーバーライドはDevToolsなどでは確認できず、!importantを付与して解決できる問題ではありません。

なお、この問題はブラウザ依存性のある問題です。ですのでブラウザの種類やバージョンによって挙動が異なります。最新のGoogle Chrome, Firefox, Operaでは確認できますが、Edge, Safariでは確認できない(=問題が発生せず、正常に動作する)ようです。また今後のバージョンアップによっても挙動が変わる可能性があります。

なお未検証ですが、filterについても同様の現象が発生するそうです。

正しく透過するためには?

3D変形する子要素を持たない要素に対してのみopacity; 0.5;を設定するようにすれば、この問題を回避できます。一つの要素の中に3D変形したい要素と半透明にしたい要素が同じレベルで共存している場合は、半透明にしたい要素を丸ごとwrapper要素で括り、それを半透明にすることで解決できます。

See the Pen Untitled by 吉川音依 (@tyvmyosm-the-scripter) on CodePen.

ちなみに、3D変形した要素を丸ごとwrapper要素で括って透過すると、以下のようになります。

See the Pen transform-style: preserve-3d; – 要素を半透明にする④(失敗例) by 吉川音依 (@tyvmyosm-the-scripter) on CodePen.

wrapper要素自体は子要素が3D変形していないため問題ありませんが、それ以前に子要素が不透過の状態でレンダリングされるので、それを丸ごと半透明にしてもサイコロの内部は映らなくなります。ちょっと難しいですが、理解できればなるほど、となる例です。