CSS3のグラデーションには線形グラデーションのlinear-gradient
と放射状グラデーションのradial-gradient
がありますが、CSS4のWorking Draftには円錐状にグラデーションさせるためのconic-gradient
という項目が存在します。ただし、conic-gradient
はまだ草案段階なので2017/7時点ではどのブラウザでも使用する事はできません。
今回、デザイナーに相談されて色々と調べてみた結果、いくつかの方法でconic-gradient
を実現する事ができたので、その方法についてご紹介したいと思います。
conic-gradientを実現する方法
私の方で調べた限りだと以下の3つの方法が見つかりました。
- CSS conic-gradient() polyfillを使用する
- CSSのclipプロパティを使って表現する(サンプル)
- SVGのマスキングを使って表現する(サンプル)
1はライブラリ依存にはなりますが、簡単&多機能なので実装方針などで問題なければ一番手軽な手段だと思います。
2はJavaScriptを使わずにCSSだけで表現できるので比較的パフォーマンスで優位な場合がありますが、clipプロパティ依存なのでIEだと正しく動作しません。
今回、私は3の方法で実装してみた(DEMO)ので、その実装方法と仕組みについて解説します。
SVGのマスキングを使って表現する方法
conic-gradient
を擬似的に表現する手順はざっくり言うと以下の通りです。各項の詳細については後述します。
- 円状にグラデーションが適用されたマスキング要素を生成する
- 図形にマスキングを適用する
- マスキングされた図形を重ねる
手順の詳細を説明する前に抑えておきたい事柄として、SVGのマスキングについて簡単に紹介しておきます。
SVGのマスキングとは
SVGのマスキングを使うと明度の度合いによって要素を部分的に透過させる事ができます。明度が低い(黒色に近い)ほど透明になり、明度が高い(白色に近い)ほど不透明になります。
以下のコードは、赤色のrect要素にマスクをかけてグラデーション状に透過させる例です。
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="Gradient">
<stop offset="0" stop-color="white" stop-opacity="0" />
<stop offset="1" stop-color="white" stop-opacity="1" />
</linearGradient>
<mask id="Mask">
<rect x="0" y="0" width="200" height="200" fill="url(#Gradient)" />
</mask>
</defs>
<rect x="0" y="0" width="200" height="200" fill="green" />
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#Mask)" />
</svg>
このマスキングを使って、conic-gradient
を擬似的に表現する方法についてご説明します。
1. 円状にグラデーションが適用されたマスキング要素を生成する
この部分がキモになるのですが、rect要素を少しずつ回転させて、且つ回転する毎に白色→黒色に変化させ、以下のようなイメージの図形を描画します。
図形を描画すると円錐状にグラデーションがかかった円が出来上がります。具体的なコード例を以下に記載します。
<svg id="mySvg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 160 160" version="1.1">
<defs>
<mask id="angle">
<g id="circleMask">
</g>
</mask>
</defs>
</svg>
const changedDegree = 255;
let mask = document.querySelector('#circleMask');
function makeRGBA(degree){
var ratio = 1 - Math.abs(degree / changedDegree);
var colorVal = Math.floor(255 * ratio);
var colorArray = [colorVal,colorVal,colorVal]
return 'rgba('+colorArray.join(',')+',1)';
}
for(i = 1 ; i < changedDegree ; i++){
let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('width', 80);
rect.setAttribute('height', 80);
rect.setAttribute('fill', makeRGBA(i));
rect.setAttribute('transform', 'rotate('+ (i + 90) +' 80 80)');
mask.appendChild(rect);
}
2. 図形にマスキングを適用する
1で作成したマスキング要素を特定のSVG要素に適用します。
<circle r="60" cx="80" cy="80" fill="orange" mask="url(#angle)"/>
そうすると、以下のようにマスキングされた状態になります。
3. マスキングされた図形を重ねる
あとはマスキングされたSVG要素と同サイズのSVG要素に重ねると出来上がりです。
<circle r="60" cx="80" cy="80" fill="red"/>
<circle r="60" cx="80" cy="80" fill="orange" mask="url(#angle)"/>
所感
conic-gradient
を表現するのは思ったより簡単ではありましたが、パフォーマンスや保守性を考慮するとlinear-gradient
、もしくはradial-gradient
で表現可能なデザインに変更する方が望ましいかもしれません。
CSS4でconic-gradient
が勧告される日を楽しみに待ちたいと思います。