最近、Webサイト制作でデザインガイドラインの一部としてティルト角(tilted angle)を頻繁に使う必要がありました。この記事で「ティルト角」とは、次のように(Webページの)上か下の辺が完全に水平ではなく少し傾斜している部分を指すものとします。
ティルト角の実装方法はたくさんあります。base64でエンコードした画像を背景に適用して(カラーや角度などの)カスタマイズで大変な思いをした経験があるかもしれません。
positionをabsoluteに指定した疑似要素(pseudo-element)にskew(ゆがみ)やrotate(回転)を適用する方法もありますが、形がゆがんで(skew transform)しまうので、これも避けたいことの1つです。
Sassを使えば、動的に生成されたSVGをエンコードする「Angled Edges」ライブラリーを利用できます。とてもよく動作しますが、幅と高さを固定でpixel指定する必要があり、悩みの種となります。
Sassでの実装が可能か、どうやって実装できるかぜひとも知りたいと思いました。シンプルなシナリオ向けには少々オーバースペックかもしれませんが、ついに自信をもってすすめられるソリューションにたどり着きました。
アイデア
私のアイデアは、positionをabsoluteに指定した疑似要素に透明度(transparent)50%と単色のグラデーション50%を適用することです。グラデーションの角度がティルト角となります。
background-image: linear-gradient($angle, $color 50%, transparent 50%);
この記述をmixinにパッケージして背景色をコンテナに適用し、与えられた角度に基づいて現在のグラデーションで疑似要素を生成します。とても簡単です。次のようにして使います。
.container {
@include tilted($angle: 3deg, $color: rgb(255, 255, 255));
}
ここで直面した課題は、疑似要素の高さをどれくらいにすべきかという点です。最初は高さをmixinの引数で指定しましたが、最適な高さを見つけるためにいろいろな角度で試行錯誤を繰り返す結果となりました。理想的ではありません。
なにもかも放り出してしまいそうになったとき、コンピューターから離れてペンと紙で基になる数式を計算してみました。「三角法」を思い出すのに少しの時間(とGoogle検索)が必要でしたが、ついにうまくいきました。
推測で適当な値をはじき出さなくてもいいように、与えられた角度から計算で疑似要素の高さを求めます。すべてはもちろん「少しのSassと多くの幾何」で達成されます。では始めます。
疑似要素の高さを計算する
実は、それほど難しくありません。疑似要素を全幅にします。グラデーションライン(gradient line)は疑似要素の対角線となり、直角三角形ができます。
この直角三角形を「ABC」とすると、角Cが直角で角Bの値($angleの引数)が決まっている場合、角Aの値はC - Bとなります。下の図を見ながら辺bの長さを求めます。
辺の長さを求めるには辺c(グラデーションライン、つまり斜辺)の長さが必要ですが、これは(底辺、100%)の長さを角aのサインで割って求められます(この例では角B = 5°なので角Aは85°です)。
c = a / sin(C - B)
ここからは「ピタゴラスの定理」を使います。
斜辺(直角と向かい合う辺)の2乗はほかの2辺の2乗の和と等しい
従って斜辺以外の一方の辺の2乗は斜辺の2乗からもう一方の辺の2乗を引いて求められます。つまり「辺bの2乗イコール辺cの2乗マイナス辺aの2乗」となります。
b² = c² - a²
最後に「辺cの2乗マイナス辺aの2乗」の平方根をとって辺bの長さを求めます。
b =√(c² - a²)
できました。これで、与えられた角度に基づいて疑似要素の高さを計算する小さなSass関数を構築できます。
@function get-tilted-height($angle) {
$a: (100% / 1%);
$A: (90deg - $angle);
$c: ($a / sin($A));
$b: sqrt(pow($c, 2) - pow($a, 2));
@return (abs($b) * 1%);
}
注:pow()、sqrt()、sin()関数はSassy-Math、Compass、custom sourcesにあります。
mixinで「tilted関数」を構築する
もう山は越えました。本当です! 最後に実用的なmixin「tilted()」を構築します。この関数はangle(角度)とcolor(カラー)を引数として受け取り、疑似要素を生成します。
@mixin tilted($angle, $color) {
$height: get-tilted-height($angle);
position: relative;
background-color: $color;
&::before {
content: '';
padding-top: $height;
position: absolute;
left: 0;
right: 0;
bottom: 100%;
background-image: linear-gradient($angle, $color 50%, transparent 50%);
}
}
注目すべき点として、このmixinではコンテナにposition: relativeを適用して疑似要素のpositionコンテキストを定義しています。absoluteやfixedとして指定された要素に上のmixinを使う場合、mixinからrelativeの宣言を削除することは大切です。
このmixinでコンテナ自体に疑似要素と同じ背景色を適用しているように、ほかの方法を使う場合でも背景色は同じにする必要があります。
最後に、疑似要素の高さはheightではなくpadding-top(場合によってはpadding-bottom)で指定しなければなりません。高さは親要素の幅に基づくパーセンテージで表されるので、heightは基準にできません(heightは親要素の高さに基づいて計算されるからです)。
まとめとさらなるステップ
記事ではシンプルなmixinを紹介しましたが、このmixinには柔軟性が欠けており、理論的に次のような問題があります。
- このmixinはすでに::before疑似要素を使用している要素には使えない。この点はオプションのパラメーターを追加し、疑似要素をデフォルトでbeforeに指定して解決できる
- コンテナの底辺がmixinのコアでbottom: 0としてハードコーディングされている場合、このmixinではティルト角で傾斜した辺を表示できない。この点はmixinにpositionを追加指定して解決できる
私の既存のバージョンはJekyllプロジェクトのSassベース数学関数を使っており、Sassレイヤのextendはできません。node-sassを使っているなら、EyeglassやSassportでこれらの関数をJavaScriptからSassに簡単に渡せるので、間違いなくもっとうまくいくでしょう。
[翻訳:新岡祐佳子/編集:Livit]