コードの臭い消し in CSS

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が勧告される日を楽しみに待ちたいと思います。

[紹介元] CSSタグが付けられた新着投稿 – Qiita コードの臭い消し in CSS

  • コメント

    1. 匿名希望
      2017/08/19(土) 03:06:46

      今カラー原稿のベタで使ってる画材が近くで見てもぜんっぜん分からなかったんだけど、あれは何を使ってるんだろう……。かなり均一で、筆塗りによる厚みのムラすら無いんだよね。グラデーションがつけられる画材っぽいので、マスキングしてエアブラシで吹いてるのかな?とか予想してる

    2. 匿名希望
      2017/08/19(土) 03:06:46

      女は描くと気分良くなるので女の裸とか自分が抜くために描きまくってましたよ、高校の時エアーブラシまで買ってもらい、マスキングまでして女のなめらかなグラデーションを表現しようとしてました。それもこれも精子を出すためです(akiman7)

    記事に戻る

関連記事