2014/12/10
HTML5のCanvasの画像処理に挑戦(透過率の変更)
最近対応ブラウザがIE9以上とその他最新のブラウザといった案件が増えてきており、
Canvasを実際のサイトに使う機会にも逢うようになりました。
そんな中、Canvasの特性を利用するために画像処理の技術がどうしても必要になるということに
今更ながら気付いたので、
その方法について調べてやってみることにしました。
今回はCanvasの画像処理の中で最も簡単だと思われる、
透過率の変更を使ったアニメーション、すなわちフェードイン・フェードアウトを
やってみましたのでご紹介いたします。
↓作ってみたもの
方法
まず、HTML、CSSを準備します。
HTML
1 2 | <canvas id="canvas" width="320" height="320"></canvas> <canvas id="dataCanvas" width="320" height="320"></canvas> |
CSS
1 2 3 4 5 6 | #canvas{ border-radius: 2px; } #dataCanvas{ display: none; } |
ここでのポイントは、実際に画像を表示するCanvasとは別に、
画像を処理するためだけの、非表示のCanvasを配置することです。
続いて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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | //画像のパスを登録 var imgsUrl = [ "images/spring1.jpg", "images/summer1.jpg", "images/autumn1.jpg", "images/winter1.jpg", "images/spring2.jpg", "images/summer2.jpg", "images/autumn2.jpg", "images/winter2.jpg", "images/spring3.jpg", "images/summer3.jpg", "images/autumn3.jpg", "images/winter3.jpg", "images/spring4.jpg", "images/summer4.jpg", "images/autumn4.jpg", "images/winter4.jpg" ], img = []; //Canvasの準備 var canvas = document.getElementById('canvas'), //メインのCanvas dataCanvas = document.getElementById('dataCanvas'); //非表示にしている処理用のCanvas var ctx = canvas.getContext('2d'), dataCtx = dataCanvas.getContext('2d'); //画像の読み込み for(var i in imgsUrl){ img[i] = new Image(); img[i].src = imgsUrl[i]; } var ImageData = {}; img[0].onload = function(){ //1つ目の画像が読み込めたら実行 ctx.drawImage(img[0], 0, 0, 320, 320); //Canvasに1つ目の画像を描画 imageData = ctx.getImageData(0, 0, img[0].width, img[0].height); //1つ目画像のデータを取得 } var flameNumSlide = 30, //アニメーションのフレーム数 slideNum = 0; //スライドのカウンターを初期化 img[1].onload = function(){ //2つ目の画像が読み込めたら実行 dataCtx.drawImage(img[1], 0, 0, 320, 320); //処理用のCanvasに2番目に表示する画像を描画 //スライドの実行 var animateSlide = setInterval(function(){ slideNum++; //スライドのカウンターを設定 if(slideNum == img.length) slideNum = 0; //最後のスライドが終わったら1つ目に戻る //フェードアウトを開始 imageFadeOut(imageData, function(){ //フェードアウトが終わったら実行 dataCtx.drawImage(img[slideNum], 0, 0, 320, 320); //処理用のCanvasに次の画像を描画 imageData = dataCtx.getImageData(0, 0, img[slideNum].width, img[slideNum].height); //処理用の画像データを取得 imageFadeIn(imageData); //フェードインを開始 }); }, 5000); } //フェードアウト function imageFadeOut(imageData, fn){ //imageFadeIn(取得した画像データ, コールバック関数) var flameNum = 30, //アニメーションのフレーム数 duration = 500, //アニメーションにかかる時間 flame = 0, //フレームのカウンターを初期化 data = imageData.data; //画像データの実データを取得 //フェードアウトを実行 var animateFade = setInterval(function(){ if(flame < flameNum){ //設定したフレーム数になるまで実行 changeOpacity(data, 1 - flame / flameNum); //透過度を変更 imageData.data = data; //変更を画像データに反映 ctx.putImageData(imageData,0,0); //変更した画像データをCanvasに描画 flame++; //フレームのカウンターを設定 }else{ //アニメーションが終わったら実行 if(fn){ //コールバック関数があるとき、関数を実行 fn(); } clearInterval(animateFade); //アニメーションを止める } }, duration / flameNum); } //フェードイン function imageFadeIn(imageData, fn){ var flameNum = 30, //アニメーションのフレーム数 duration = 500, //アニメーションにかかる時間 flame = 0, //フレームのカウンターを初期化 data = imageData.data; //画像データの実データを取得 //フェードインを実行 var animateFade = setInterval(function(){ if(flame < flameNum){ //設定したフレーム数になるまで実行 changeOpacity(data, flame / flameNum); //透過度を変更 imageData.data = data; //変更を画像データに反映 ctx.putImageData(imageData, 0, 0); //変更した画像データをCanvasに描画 flame++; //フレームのカウンターを設定 }else{ //アニメーションが終わったら実行 if(fn){ //コールバック関数があるとき、関数を実行 fn(); } clearInterval(animateFade); //アニメーションを止める } }, duration / flameNum); } //透過度変更 function changeOpacity(data, opacity){ //changeOpacity(画像の実データ, 透過度) var alphaCh = Math.round(255 * opacity); //透過度をアルファチャンネルの値に変換 for(var i = 0; i < data.length; i += 4){ //各ピクセルのアルファチャンネルの数実行 data[i + 3] = alphaCh; //アルファチャンネルの値を変更 } } |
この中で、画像の透過処理の流れについてご説明いたします。
大きな流れは次の通り。
- 処理用のCanvasに画像を読み込み、仮想で表示する
- 処理用のCanvasから画像データを取得
- 画像データの透過度を変更する処理を行う
- 変更した画像データを表示用のCanvasに表示する
1. 処理用のCanvasに画像を読み込み、仮想で表示する
Canvasでフェードイン・フェードアウトの処理をする場合、
画像処理のため、一度処理前の画像をCanvas上に表示し、
そこから画像データを取得する必要があります。
フェードインのときの画像は、初めの段階ですでに透過度が0に変更されたものを表示しなければちらつきが発生してしまいます。
そのため、CSSでdisplay:noneに設定した処理用のCanvasに画像を読み込んで、仮想で表示するという処理が必要になります。
2. 処理用のCanvasから画像データを取得
処理用のCanvasに画像を仮想で表示したら、getImageData()というメソッドを使って画像データを取得します。
1 2 | imageData = dataCtx.getImageData(取得する位置(x座標), 取得する位置(y座標), 取得する横幅(width), 取得する高さ(height)); //dataCtxは任意の名前で用意した仮想のCanvasオブジェクト data = imageData.data; //画像データの実データを取得 |
3. 画像データの透過度を変更する処理を行う
取得した画像データに変更を加えることで、透過度変更の画像処理を行います。
getImageData()で取得されるデータの中にdataというメソッドがあります。
dataメソッドは256段階の色情報がRGBA(赤、緑、青、透過度)の順番で1次元的に並んだものです。
そのため、透過度を変更するには、このデータの中で、4つ飛ばしで存在する透過度の情報だけを変更すればいいことになります。
1 2 3 | for(var i = 0; i < data.length; i += 4){ //各ピクセルのアルファチャンネルの数実行 data[i + 3] = 0; //アルファチャンネルの値を変更 } |
今回はFOR文を使ってこれを行いました。
上の例の場合、透過度が0に設定されるので、画像が完全な透明になります。
data[i + 3] = 127;にすれば半透明、data[i + 3] = 255;にすれば完全な不透明になるといった具合です。
4. 変更した画像データを表示用のCanvasに表示する
データの変更をしたら、そのデータをputImageData()というメソッドを使って表示用のCanvasに表示します。
1 2 | imageData.data = data; //変更を画像データに反映 ctx.putImageData(imageData, 0, 0); //変更した画像データをCanvasに描画 ctxは任意の名前で用意した表示用のCanvasオブジェクト |
単純な処理ではありますが、
画像処理の仕組みの理解と練習にはちょうどいいと思いますので、
是非試してみてください。
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE