Analog Studio

CSSのグラデーションに縞模様(バンディング)が出てしまった時の対処法【gradient】

概要

CSS のグラデーション関数は便利ですよね。最近の Web デザインではボーダーやボタンなどにグラデーションが付いていることも増えてきました。
画像の作成が必要なく修正も用意で、簡単にオシャレにできるので人気があります。

しかし、グラデーションを使っていて縞模様が気になってしまった…という経験をお持ちの方も多いのではないでしょうか?
この現象はバンディングやマッハバンドと呼ばれ Photoshop などでも発生してしまうなかなか厄介なものです。
生成されたイメージの階調が少なく淡いグラデーションではどうしても気になってしまい根本的には解決できません。

そこで今回はそんなバンディングを目立たなくする手法を紹介します。

バンディングってどんな縞模様なのか?

まずは以下の画像をご確認下さい。
薄っすらと縞模様が見えるでしょう。色の設定によってはもっと顕著に表れることもあります。

バンディングが発生したグラデーション (radial-gradient)

ちょっと分かりにくいかもしれませんので強調したものが以下になります。

バンディングを強調したイメージ

このイメージは CSS のグラデーションでは以下の設定で生成しています。
ブラウザや画面サイズ、ディスプレイによってはバンディングが発生しない (わかりにくい) こともあります。

.banding{ background : radial-gradient(circle, rgba(0,0,0,0.1) 26%, rgba(0,0,0,0.6)); }

ノイズを意図的に重ねることで目立たなくなる!

バンディングは急に色が変わることでマッハバンドと呼ばれる錯視が発生し境目が目立ってしまうことで起こります。
そこでグラデーションに意図的に微量のノイズを重畳することで境目を分かりにくくしてしまう手法が良く取られます。

SVGでノイズ画像を生成する

背景が透明なノイズ画像を予め作成しておいても良いですが、ここでは SVG を使ってノイズを生成した例を使います。
使用するノイズ SVG は以下の通りです。feTurbulence フィルタでノイズパターンを作成し、ガンマ調整で粒を目立たせています。
SVG コードの詳細は調べてみて下さい。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100"> <defs> <filter id="noise" filterUnits="userSpaceOnUse" x="0" y="0" width="100" height="100"> <feTurbulence type="turbulence" baseFrequency="0.5" stitchTiles="stitch" result="gamma"></feTurbulence> <feComponentTransfer in="gamma" result="noise"> <feFuncA type="gamma" amplitude="50" exponent="5" offset="-0.5"></feFuncA> </feComponentTransfer> </filter> </defs> <rect filter="url(#noise)" x="0" y="0" width="100" height="100" opacity="1.0"></rect> </svg>

このコードで生成されるノイズは以下のものです。
分かりやすく枠線を表示していますが実際には枠線はありません。

DataURLに変換してCSSにノイズ画像を埋め込めるようにする

グラデーションに画像を重ねるには、上記コードを "*.svg" の名前で保存しておくか DataURL に変換して background プロパティに追加することでノイズをグラデーションに重ねます。

dataURL に変換する方法は以下の2通りありますが、好きな方で構いません。
Base64 変換したものが一般的ですが、URL エンコードしただけのものは後から変更がしやすいメリットがあるので適宜選択して下さい。

Base64 変換した DataURL を簡単に取得できるツールは沢山ありますが、私はいつも以下を使わせて頂いています。ありがとうございます。
・SYNCER - Base64 エンコーダー (画像を簡単に変換)

<XMLをURL変換したもの> // SVG は XML で書かれている (バイナリデータでない) ので、そのまま DataURL として利用可能です // ただし、IE などの古いブラウザでは URL エンコードされていないとうまく動きませんのでエンコードします // Javascript では encodeURIComponent( SVGコード ) で変換できます // URLエンコードした文字列の先頭に "data:image/svg+xml," を加えれば使用できます // ※以下は改行を挿入していますが実際に使う場合は改行は削除して下さい data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100 %20100%22%20width%3D%22100%22%20height%3D%22100%22%3E%0A%20%20%3Cdefs%3E%0A%20%20%20%20%3Cfilter%20id%3D% 22noise%22%20filterUnits%3D%22userSpaceOnUse%22%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height% 3D%22100%22%3E%0A%20%20%20%20%20%20%3CfeTurbulence%20type%3D%22turbulence%22%20baseFrequency%3D%220.5%22% 20stitchTiles%3D%22stitch%22%20result%3D%22gamma%22%3E%3C%2FfeTurbulence%3E%0A%20%20%20%20%20%20%3CfeComp onentTransfer%20in%3D%22gamma%22%20result%3D%22noise%22%3E%0A%20%20%20%20%20%20%20%20%3CfeFuncA%20type%3D %22gamma%22%20amplitude%3D%2250%22%20exponent%3D%225%22%20offset%3D%22-0.5%22%3E%3C%2FfeFuncA%3E%0A%20%20 %20%20%20%20%3C%2FfeComponentTransfer%3E%0A%20%20%20%20%3C%2Ffilter%3E%0A%20%20%3C%2Fdefs%3E%0A%20%20%3Cr ect%20filter%3D%22url(%23noise)%22%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height%3D%22100%22%2 0opacity%3D%221.0%22%3E%3C%2Frect%3E%0A%3C%2Fsvg%3E <SVGをBase64エンコードしたもの> // Javascript では btoa( SVGコード ) で変換できます // Base64エンコードした文字列の先頭に "data:image/svg+xml;base64," を加えれば使用できます // ※改行はそのままでも動作しますが念のため削除して下さい data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTA wIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+DQogIDxkZWZzPg0KICAgIDxmaWx0ZXIgaWQ9Im5vaXNlIiBmaWx0ZXJVbml0cz0idXNl clNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4NCiAgICAgIDxmZVR1cmJ1bGVuY2UgdHlwZT0id HVyYnVsZW5jZSIgYmFzZUZyZXF1ZW5jeT0iMC41IiBzdGl0Y2hUaWxlcz0ic3RpdGNoIiByZXN1bHQ9ImdhbW1hIj48L2ZlVHVyYnVsZW 5jZT4NCiAgICAgIDxmZUNvbXBvbmVudFRyYW5zZmVyIGluPSJnYW1tYSIgcmVzdWx0PSJub2lzZSI+DQogICAgICAgIDxmZUZ1bmNBIHR 5cGU9ImdhbW1hIiBhbXBsaXR1ZGU9IjUwIiBleHBvbmVudD0iNSIgb2Zmc2V0PSItMC41Ij48L2ZlRnVuY0E+DQogICAgICA8L2ZlQ29t cG9uZW50VHJhbnNmZXI+DQogICAgPC9maWx0ZXI+DQogIDwvZGVmcz4NCiAgPHJlY3QgZmlsdGVyPSJ1cmwoI25vaXNlKSIgeD0iMCIge T0iMCIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIG9wYWNpdHk9IjEuMCI+PC9yZWN0Pg0KPC9zdmc+

グラデーションとノイズ画像を重ね合わせる

この SVG を利用してグラデーションにノイズを加えます。
background プロパティ値に url( 'SVG の URL' ), を ***-gradient() 関数の前に追加するだけです。
CSS の background プロパティ値はより前に記述されたものほど全面に配置されますので、注意して下さい。

.banding-noise{ background : url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cD…'), radial-gradient(circle, rgba(0,0,0,0.1) 26%, rgba(0,0,0,0.6)); }

できましたね!グラデーションとノイズが重なりました。
しかし、このままではノイズが目立ってしまいバンディングよりも気になります。

ぼかしを加えてノイズを目立たなくする

最後にグラデーションとノイズを合わせ、ぼかしてノイズが目立たないようにしていきます。
CSS の filter : blur() を使うだけでぼかしが入れられます。ぼかし具合は適宜調整して下さい。

.banding-noise-blur{ background : url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cD…'), radial-gradient(circle, rgba(0,0,0,0.1) 26%, rgba(0,0,0,0.6)); filter : blur(5px); }

ぼかしてノイズもバンディングも目立たなくなりましたね!
しかし、要素の端もボケてしまったので必要なら修正していきます。

※ IE では filter プロパティが使えないので、SVG にガウスぼかしフィルターを追加しても良いかもしれません。この場合は filter プロパティは削除して下さい。

<!-- ガウスぼかしフィルターを追加する場合 --> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100"> <defs> <filter id="noise" filterUnits="userSpaceOnUse" x="0" y="0" width="100" height="100"> <feTurbulence type="turbulence" baseFrequency="0.5" stitchTiles="stitch" result="gamma"></feTurbulence> <feComponentTransfer in="gamma" result="blur"> <feFuncA type="gamma" amplitude="50" exponent="5" offset="-0.5"></feFuncA> </feComponentTransfer> <feGaussianBlur in="blur" result="noise" stdDeviation="5"></feGaussianBlur> </filter> </defs> <rect filter="url(#noise)" x="0" y="0" width="100" height="100" opacity="1.0"></rect> </svg>

はみ出したボケを取り除く

これには overflow プロパティを使いますが、一工夫必要です。
そのままボックスに指定してもボックス外のボケは消えてくれません。

そこで疑似要素を利用します。
疑似要素を親ボックスと同じか一回り大きくしてこちらにぼかしを入れます。(ぼかしと同じ大きさだけ大きくすると良いです)
親要素からはみ出した部分は overflow : hidden; で隠すことができます。

.banding-noise-blur-border{ position : relative; overflow : hidden; } .banding-noise-blur-border::before{ content : ''; position : absolute; top : -5px; bottom : -5px; left : -5px; right : -5px; z-index : 0; background : url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cD…'), radial-gradient(circle, rgba(0,0,0,0.1) 26%, rgba(0,0,0,0.6)); filter : blur(5px); }

まとめ

以上、CSS のグラデーションで段々の縞模様、バンディングが発生してしまった場合の対処法でした。
ご質問等あればお気軽にコメントを頂ければと思います。