とほほえんどの術
とほほな結末でよく使われるエフェクト「アイリスアウト(アイリスワイプ)」。アイリスとはレンズの絞りのことで、カメラレンズの絞りを閉じていく様子を模した場面転換の手法です。そんなエフェクトをCSSで表現する手段を3つ記します:)。
仕組み
ここでは、場面転換からは少しスケールを落として、下図の「とほほなパンちゃん」画像にカーソルを合わせる(かタップするかする)と、パンちゃんの顔の部分が丸くフォーカスされるっていうサンプルを通して、アイリスアウトを作る3つの手段の、仕組みとコツを記してゆきます。
HTMLは下ソースコードのように、figure要素の中に、img要素と、アイリス用のdiv要素(以下、アイリス要素と呼ぶ)を入れます。アイリス要素の中に「とほほー!」という台詞を入れておいて、アイリスが閉じた時にアイリスの上に重なるっていう仕組みです:)。
透明な丸に落ちる影
要素の中央を丸くくり抜くって考えると難しそうだけれど、丸型の周りを塗りつぶすと考えると分かり良いかもしれません。
アイリスとなる面はbox-shadowプロパティを使って作ります。box-shadowプロパティは、透明な要素にも影を落とすことができるってトコがポイント;)。
アイリス要素自体はfigure要素を覆うように絶対配置しておいて、アイリス面は::after擬似要素で作ります。
擬似要素で、画像の対角線と同じ直径の正円を作って、「のどかな空色の影」を落とします。影をボカさずめいっぱい広げれば、中央に丸い穴が空いたベタ塗りの出来上がり;D。
ここでは、アイリス要素のサイズを--size
というCSS変数で管理してます。親要素のサイズを決め打ちしておくことで、内部の要素サイズを調整しやすくなります。
要素の比率を3:2
になるよう決め打ちしてるので、横幅の1.2倍くらいが、ちょうど対角線の長さになるはずです。ここではsqrt()
という、平方根を返す指数関数ってのを使って、より正確に対角線の長さを指定しています。
sqrt()について詳しくは下記ページを参照のこと。
sqrt() - CSS: Cascading Style Sheets | MDN
影の広さもCSS変数を利用すれば、画像を覆い隠せるサイズの影にできますね;)。
タップした時に、アイリス要素の横幅を小さくしてゆけば、アイリスが絞られていく様子を表現できます。ここにもCSS変数を利用して、画像の幅の36%
の幅(パンちゃんの顔が入るくらいのサイズ感)にしています。
要素の中央に配置されるようにしたいので、アイリス要素をグリッドにして、place-contentプロパティの値をcenter
としておけば、擬似要素は、アイリス要素をハミ出しても常に中央に配置されるようになります;)。
アイリスの位置は、top/leftプロパティで調整します。ここでは少し横着してinsetプロパティで一括指定しています。
あとは台詞を、タップしてから表示されるよう指定したら、出来上がり:D!
画像をクリッピングする
要素の穴から画像を覗かせるのではなくて、画像自体を丸くくり抜いちゃう方法です。台詞は画像の下に隠れるので、事前に非表示にしておかなくても良いのが良いところ。
clip-pathプロパティの値をcircle()
関数を使って指定すれば、要素を丸くクリッピングすることができます。circle()
の括弧の中に指定する値は円の半径なので、前章でアイリス要素に指定したサイズの半分(対角線の長さの半分)を指定すれば、アイリス要素の外接円でクリッピングすることができます。(下サンプルひとつめ)
タップした時のサイズも、前章に倣って、その半分のサイズを指定します(calc(var(--size) * .36)
だったのでcalc(var(--size) * .18)
)。
circle()
の値にcalc(100% / sqrt(2))
という値を指定しても、要素の外接円を適用することができます。こちらの方が汎用性があるかも。(下サンプルふたつめ)
circle()
は、円の中心点をat X Y
という風に座標でもって指定することができます。ここでは、タップする前は50% 50%
、タップしたら34.7% 41.4%
となるよう、半径の指定の後に追記します。
できあがり:D!
マスクで空ける穴
要素に丸く穴を空けて画像を覗かせる方法。ちゃんとアイリスで画像を隠す構造だし、アイリス要素内の台詞も一緒にマスクされるから、box-shadowとclip-pathの良いとこ取りみたいな仕組みです。
maskプロパティのマスクの型となる画像を、radial-gradient()関数を使って指定します。透明なところが非表示になって、不透明なところが表示されるので、中央が透明で外側が不透明な円を描けば良いですね:D。
#0000
(透明な黒)から外へ向かって#000
(黒)へグラデーションさせつつ、色経由点(始点と終点)を同じ値にすることでパキッと切り替わるようにします。
色経由点は、要素の中央から要素の四隅までの距離、つまり対角線の半分を指定すれば良いので、clip-pathプロパティの時と同じサイズにすれば外接円が描けるはずです。
画像の表示位置はcenter
、画像サイズは縦も横もアイリス要素の横幅と同じ大きさ(var(--size)
)にしておきます。
タップした時には、グラデーションの色経由点を要素の内側へ萎めることで、円形を小さくします。ここも、clip-pathプロパティの時と同じサイズを指定しています。
けどアニメーションしません。radial-gradient()
内の値は、アニメーションがサポートされていないんですね:((2024.11現在)。
しかし、変化させたい値をカスタムプロパティとして定義する事で、アニメーションさせることができます:D! @propertyルールで、プロパティを定義します。定義しなきゃならない決め事は以下の3つ。
syntax
- どのような値を受け入れるか。
inherits
- 指定した値を子要素に継承するか否か。
initial-value
- プロパティの初期値。
ここでは、以下のように指定しています。
syntax
に、<length>
や<number>
や<color>
といったデータ型を指定したプロパティは、アニメーション可能なプロパティとなります。<length-percentage>
は数値も割合も受け入れるというデータ型で、アニメーションも可能になります。
@propertyルールについて詳しくは下記ページを参照のこと。
@property - CSS: カスケーディングスタイルシート | MDN
定義した--radius
というカスタムプロパティを、radial-gradient()
に指定する色経由点の値として使います。
それからtransition
も--radius
で指定し直して、タップした時は--radius
の値を更新します(下サンプルひとつめ)。
radial-gradient()
は、色経由点の前に、circle at X Y
というふうに指定すれば、その要素内の座標でもって円の中心点を指定することができます。
ここでは、タップした後の中心点を34.7% 44.3%
と指定しています(下サンプルふたつめ)。
そうです、radial-gradient()
がアニメーションをサポートしていないので、中心点ももちろんアニメーションしません。そんなわけで、新たに--x
と--y
というカスタムプロパティも定義します…!
出来ました!
あとがき
maskプロパティを使った方法は使い勝手が良いけれど、古めのiPhoneやSafariでは@property
をサポートしていないことがあるので、box-shadowプロパティを使った方法かclip-pathプロパティを使った方法でのフォローが必要かもしれません。心配な時はJavaScriptを使って、@property
をサポートしているかどうかを判別した上で実装すると良いです:)。
最後まで読んでいただきありがとうございました!
以上、「とほほえんどの術」でした。