LoginSignup
12
12

More than 5 years have passed since last update.

CSS4のconic-gradientを先取りする方法

Last updated at Posted at 2017-07-12

CSS3のグラデーションには線形グラデーションのlinear-gradientと放射状グラデーションのradial-gradientがありますが、CSS4のWorking Draftには円錐状にグラデーションさせるためのconic-gradientという項目が存在します。ただし、conic-gradientはまだ草案段階なので2017/7時点ではどのブラウザでも使用する事はできません。

スクリーンショット 2017-07-12 23.52.22.png

今回、デザイナーに相談されて色々と調べてみた結果、いくつかの方法でconic-gradientを実現する事ができたので、その方法についてご紹介したいと思います。

conic-gradientを実現する方法

私の方で調べた限りだと以下の3つの方法が見つかりました。

  1. CSS conic-gradient() polyfillを使用する
  2. CSSのclipプロパティを使って表現する(サンプル
  3. SVGのマスキングを使って表現する(サンプル

1はライブラリ依存にはなりますが、簡単&多機能なので実装方針などで問題なければ一番手軽な手段だと思います。
2はJavaScriptを使わずにCSSだけで表現できるので比較的パフォーマンスで優位な場合がありますが、clipプロパティ依存なのでIEだと正しく動作しません。
今回、私は3の方法で実装してみた(DEMO)ので、その実装方法と仕組みについて解説します。

SVGのマスキングを使って表現する方法

conic-gradientを擬似的に表現する手順はざっくり言うと以下の通りです。各項の詳細については後述します。

  1. 円状にグラデーションが適用されたマスキング要素を生成する
  2. 図形にマスキングを適用する
  3. マスキングされた図形を重ねる

手順の詳細を説明する前に抑えておきたい事柄として、SVGのマスキングについて簡単に紹介しておきます。

SVGのマスキングとは

SVGのマスキングを使うと明度の度合いによって要素を部分的に透過させる事ができます。明度が低い(黒色に近い)ほど透明になり、明度が高い(白色に近い)ほど不透明になります。
以下のコードは、赤色のrect要素にマスクをかけてグラデーション状に透過させる例です。

HTML
<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>

image.png

参考:クリッピングとマスキング - SVG | MDN

このマスキングを使って、conic-gradientを擬似的に表現する方法についてご説明します。

1. 円状にグラデーションが適用されたマスキング要素を生成する

この部分がキモになるのですが、rect要素を少しずつ回転させて、且つ回転する毎に白色→黒色に変化させ、以下のようなイメージの図形を描画します。

スクリーンショット 2017-07-09 20.52.05.png

図形を描画すると円錐状にグラデーションがかかった円が出来上がります。具体的なコード例を以下に記載します。

HTML
<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>
JavaScript
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要素に適用します。

HTML
<circle r="60" cx="80" cy="80" fill="orange" mask="url(#angle)"/>

そうすると、以下のようにマスキングされた状態になります。

スクリーンショット 2017-07-09 18.52.55.png

3. マスキングされた図形を重ねる

あとはマスキングされたSVG要素と同サイズのSVG要素に重ねると出来上がりです。

HTML
<circle r="60" cx="80" cy="80" fill="red"/>
<circle r="60" cx="80" cy="80" fill="orange" mask="url(#angle)"/>

スクリーンショット 2017-07-09 18.57.03.png

所感

conic-gradientを表現するのは思ったより簡単ではありましたが、パフォーマンスや保守性を考慮するとlinear-gradient、もしくはradial-gradientで表現可能なデザインに変更する方が望ましいかもしれません。
CSS4でconic-gradientが勧告される日を楽しみに待ちたいと思います。

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12