この記事は Snow Monkey / unitone Advent Calendar 2024 18日目の記事です。まだ空きがあるので、ぜひお気軽にご参加ください!
Snow Monkey Blocks のセクションブロックにもセクション間を波型や斜めに区切る(以降セクション区切りと呼びます)機能があり、これはセクションと同じ色の SVG をセクションに「被せる」形で実装していました。
背景色がベタ塗りならこれでも問題ないのですが、グラデーションであったり背景画像だったりした場合、SVG にグラデーションや背景画像を反映させることはできないので諦めるしかありませんでした。
そこで、unitone はグラデーションや背景画像の場合でもちゃんと区切れるように、セクションを「マスク」する形で実装してみました。今回はその解説を書きたいと思います。
clip-path
clip-path
は、要素のどの部分を表示するかを指定できる CSS プロパティです。指定の仕方は色々あるのですが、unitone では要素上に複数の座標(要素の大きさに対する相対値で指定)を指定して、その座標同士を結ぶパスで要素の表示領域を指定できる polygon()
を使っています。
例えば、座標を3点指定したら三角形でマスクされます。
// x座標 y座標 の1セットで座標を指定し、カンマで区切って複数箇所指定できます
clip-path: polygon(0% 0%, 0% 100%, 50% 100%);
unitone では区切りの種類として「三角」を選んだときに clip-path
が使われるようになっています。
最初は「斜め」「三角」「大きい三角」みたいな選択肢をつくろうと思っていたのですが、三角の3点の座標を動かせば、三角も斜めも両方実現できるんじゃね?と思い、試してみたらできたので「三角」だけ選択肢がある状態になりました。
下記の画像のように7つの座標を指定しています。四隅の青い座標は固定で、赤い座標は設定パネルの設定に応じてそれぞれ矢印の方向に動くようになっています。
赤い座標の上2つを一番左端と右端まで動かして、真ん中の赤い座標を右端か左端のどちらかに動かせば斜めの区切りにすることができます。
下記は斜めにしたものと小さい三角にしたもののサンプルです。
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cill
大きなのがいるんだから小さいのもいるんでしょうか女の子がそっとカムパネルラにたずねました。あら、蠍の火だなカムパネルラがまた何気なくしかるように叫びました。
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cill
mask-image
波型も clip-path
でやろうと思っていたのですが、座標を結ぶパスは直線にしかできない(SVG のように曲線やベジェ曲線にはできない)ので、滑らかな波型にしようとするとかなり多くの座標を指定しなくてはならず、座標固定ならまだしも、設定パネルで大きさ等を変えたいとなるとどう考えても無理そう、ということで、別の方法がないか調べてみました。
結果、色々ハマりどころはあったものの、mask-image
と SVG の組み合わせで良い感じになりそうということがわかり、その形で実装しました。
ハマりどころ1:SVG の色がついている部分がマスクされる(非表示になる)
あんまり深く考えずに、とりあえず下記の SVG を用意しました。
ほんでこの SVG でマスクすると…
😇 ギャク〜
ということは色を反転させた SVG をつくれば良いのか?と思ってやってみたのですが、セクションの大きさは中のコンテンツによって変わるわけなので、それにあわせて SVG が伸縮して波の形が崩れてしまいます。んー。
また調べてみると、mask-composite
という、マスクの重なった部分をどう表示するかを指定できるCSS プロパティがあるのを見つけました。
この mask-composite
に exclude
という重なっていない部分だけをマスクとして使用する指定があるので、前述の波型の SVG と、四角の SVG(これは cover
にして要素全体を覆うようにする)を重ねて、その重なった部分だけをマスクとして使用すれば良い感じになるやん!と考えました。
+
やってみたらこんな感じ。ついでに波のサイズも mask-size
ちょっと調整して、mask-repeat
で横に繰り返すようにしてみる。
良い!
ハマりどころ2:マスクの境界線が微妙に見えるときがある
これ iPhone で見たときに特に顕著だったのですが、mask-composite
するために指定している複数のマスク画像の境界線が微妙〜に見えてしまうことがありました。なんというか、境界線に 1px より細い線がピーっと伸びているみたいな。
なんでこうなるのかはよくわからないのですが、SVG だけど画像の縁が滲んだりとかあるのかな?とか思い、mask-position
で各マスク画像の位置を指定するときにピッタリではなくて、0.3px〜1px とかちょっとだけ多めに重ねるようにすると良い感じに表示されました。
あと、iPhone だと小さいマスク画像を repeat-x
したときもその繰り返しの境界線が見えてしまうことがあったので、マスク画像のサイズを大きくして対応しました。
ハマりどころ3:mask-image プロパティの記述はあるけど SVG は空にしておきたいときに何を指定すれば良いかわからない
まぁそんな感じで重ねて mask-composite
すればなんとかなることがわかったので、あとはセクションの下側の区切りも指定できるように、こんな指定にしてみました。
mask-image:
var(--unitone--section-divider-top-mask-image),
url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><rect x="0" y="0" height="100" width="100"/></svg>'),
var(--unitone--section-divider-bottom-mask-image);
ほんで、設定パネルで上区切りの指定がされたときは var(--unitone--section-divider-top-mask-image)
にさっきの波型の SVG を入れれば良いし、下区切りが指定されたときも同じように var(--unitone--section-divider-bottom-mask-image)
に入れれば良いと。何も無いときは空にしとけば大丈夫よね。フォールバック値として url()
を指定しておこう。
ということで、上にだけ入れて下に入れなかったらこんな感じになります。
ちゃんと表示されていますか?表示されているという方!それはパソコンでみたときだけです!😇iPhone でみると何も表示されません😇
何かは入れないといけないけど何を入れたら良いのかわからない問題、ググってもそんなニッチな情報は見つからないので手を動かして色々試してみた結果、空の SVG を入れるのが安定っぽいということがわかりました。
--unitone--section-divider-top-mask-image: url('data:image/svg+xml,<svg/>');
--unitone--section-divider-bottom-mask-image: url('data:image/svg+xml,<svg/>');
mask-image:
var(--unitone--section-divider-top-mask-image),
url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><rect x="0" y="0" height="100" width="100"/></svg>'),
var(--unitone--section-divider-bottom-mask-image);
ということで完成したのが下記。
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cill
大きなのがいるんだから小さいのもいるんでしょうか女の子がそっとカムパネルラにたずねました。あら、蠍の火だなカムパネルラがまた何気なくしかるように叫びました。
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cill
僕が CSS マスクの知識が無さすぎたので、他にも小さいハマりポイントがいくつかあったりもしましたが、とりあえず(ある程度の)理想の形に持っていくことができました。
コードもだいぶ端折って書いたので、完全版が見たい方は GitHub で見るか、unitone をダウンロードして見てみてください!(なんと無料!)
コメントを残す