2016/02/24
【Canvas】円でぼかしのような視覚効果
メインの被写体に注目させたり、遠近感を出すとき、
背景をぼかす効果が威力を発揮します。
背景にあるものをぼかす場合、
以下の様なぼかしの方法(Photoshopでいうガウス)が、一番良く使われると思います。
この効果をHTML5のCanvasで使うと表現の幅が広がって良さそうだと思ったのですが、
Canvasでこの効果を再現しようとすると、非常に演算が重くなるという難点があり、
CSSやSVGのfilterでもその問題は同様です。
そこで、ほかにぼかしの効果として使えるものはないかといろいろ試している中、
カメラで撮影した場合に見られる、以下の様なぼかしの効果に今更ながら気づきました。
↓↓ 拡大 ↓↓
ボケたところは単純にぼやけているわけではなく、
カメラの絞りの形が重なり合ったようになっているんですね。
特に明るいところではこの形がはっきりとしています。
焦点が外れたところほど、その形は大きくなっています。
(ちなみにPhotoshopにも、「ぼかし(レンズ)」というフィルターが用意されています)
これに気づいて、円形をランダムに散りばめたように描画することで、
ぼかしの効果が再現できるのではないかと思って、
Canvasを使ってやってみました。
↓作ってみたもの
↓できた画像
方法
方法は以下のように、焦点の違う場所ごとに画像を準備し、
それぞれの画像をCanvasに読み込んで、
焦点が外れたところほど大きな円で描画するというものです。
Canvasのコードは以下のとおり
HTML
1 2 | <figure><canvas id="canvas" width="640" height="480"></canvas></figure> <figure class="hide"><canvas id="loadCanvas" width="640" height="480"></canvas></figure> |
まずHTMLでは、表示用のCanvasと、画像の情報を取得するためのCanvasを準備し、
情報取得用のCanvasはCSSで非表示にします。
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | //Canvasの準備 var $canvasElm = document.getElementById("canvas"), ctx = $canvasElm.getContext("2d"), $loadCanvasElm = document.getElementById("loadCanvas"), loadCtx = $loadCanvasElm.getContext("2d"), img = new Image(); img.src = "images/image01.png", //1番奥の画像 imageData = {}; /* 画像を描画 */ img.onload = function() { loadCtx.drawImage(img, 0, 0, 640, 480); imageData = loadCtx.getImageData(0, 0, 640, 480); //画像の情報を取得 //5px単位で画像を処理 for(var x = 0; x < 640; x+=5) { for(var y = 0; y < 480; y+=5) { //ランダムな配置になるように、X座標、Y座標、半径を決めて、描画用の関数に渡す drawCircle(Math.round(x + Math.random() * (5 - (-5)) + -5), Math.round(y + Math.random() * (5 - (-5)) + -5), Math.random() * (8 - 4) + 4); } } img.src = "images/image02.png" //2番めの画像は焦点があっている想定なので、効果をかけずにそのままCanvasに描画する img.onload = function() { ctx.drawImage(img, 0, 0, 640, 480); img.src = "images/image03.png" //1番手前の画像は焦点が一番奥の画像よりは合っている想定なので、一番目よりも細かく描画する img.onload = function() { loadCtx.clearRect(0, 0, 640, 480); loadCtx.drawImage(img, 0, 0, 640, 480); imageData = loadCtx.getImageData(0, 0, 640, 480); for(var x = 0; x < 640; x+=2) { for(var y = 0; y < 480; y+=2) { drawCircle(Math.round(x + Math.random() * (2 - (-2)) + -2), Math.round(y + Math.random() * (2 - (-2)) + -2), Math.random() * (4 - 1) + 1); } } } } } function drawCircle(x, y, r) { //描画用の関数 var color = { "r": 0, "g": 0, "b": 0, "a": 0 }; //座標からピクセルデータの場所を計算し、色の情報を取得 color = { "r": imageData.data[x * 4 + 640 * 4 * y], "g": imageData.data[x * 4 + 640 * 4 * y + 1], "b": imageData.data[x * 4 + 640 * 4 * y + 2], "a": imageData.data[x * 4 + 640 * 4 * y + 3] }; //アルファチャンネルが0(透明)でない場合のみ描画する if(color.a != 0) { var maxColorNum = 0, minColorNum = 255; for(var key in color) { if(key != "a" && color[key] > maxColorNum) maxColorNum = color[key]; if(key != "a" && color[key] < minColorNum) minColorNum = color[key]; } color = { "r": color.r, "g": color.g, "b": color.b, "a": 255 - minColorNum //暗さを透明度に変換 } //取得した色の情報で円を描画 ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.fillStyle = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + color.a + ")"; ctx.fill(); ctx.closePath(); } } |
JavaScriptの処理はおおよそ以下の様な流れです。
- Canvasの基本的な準備(要素を指定して、コンテキストを定義)をして、情報取得用の方のCanvasに画像を読み込みます
- 画像の色の情報を取得し、取得した場所に焦点のハズレ具合に応じた細かさの円を描画します
- 上記を繰り返し、奥から手前の順番で描画を行っていきます
実際やってみると、一般的なぼかしほどではないものの、それなりに処理に時間はかかるので、
動きの大きなものには向かないと思いました。
ただ、独特の雰囲気が出るので、
雰囲気がデザインにハマるようなら使うのもありかもしれないと思いました。
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE