HTML5のCanvasでつくるダイナミックな表現―CreateJSを使う

第8回 ぼかしフィルタとアルファマスク

この記事を読むのに必要な時間:およそ 10 分

BoxBlurFilterクラスで画像イメージをぼかす

では,早速BoxBlurFilterクラスでBitmapインスタンスのイメージをぼかしてみたい。だがその前に,ひとつ重要な注意を覚えていただきたい。EaselJSのコンパクト版JavaScript(JS)ファイル(easeljs-X.X.X.min.js)にはフィルタのクラスが基本的に含まれないということだ※2⁠。つまり,使うフィルタのクラスはscript要素でひとつひとつ読込まなければならない。

<script src="easeljs/filters/BoxBlurFilter.js"></script>

フィルタをかけるには,大きく3つの手順を踏む。第1に,フィルタのインスタンスをつくる。お約束どおり,コンストラクタの呼出しだ。BoxBlurFilter()コンストラクタには3つの引数が渡せる。初めのふたつは,それぞれ水平と垂直のぼかし幅で,単位はピクセルだ。3つ目は,ぼかしのきめ細かさを1から3の整数を目安として与える。この整数は,内部的にぼかしを重ねる回数になるので,増やせば負荷が高まる。

new BoxBlurFilter(水平ぼかし, 垂直ぼかし, ぼかし品質)

手順の第2は,フィルタオブジェクトをDisplayObject.filtersプロパティに定める。ただし,プロパティにはフィルタオブジェクトを直に与えるのでなく,配列に入れたうえで,その配列を代入する。これは,フィルタを複数かけられるように考えられた仕組みだ。

そして第3に,設定されたフィルタをDisplayObject.cache()メソッドで描画する。内部的には,Canvasが新たにつくられ,そこでフィルタの演算と描画を行った後,ステージにその結果が加えられる。引数は矩形領域を示す4つの数値で,その範囲にフィルタがかかる。

DisplayObjectオブジェクト.cache(x座標, y座標, 幅, 高さ)

LoadQueue.fileloadイベントのリスナーに定めた描画の関数(draw())には,つぎのようなJavaScriptコードを加えよう。BoxBlurFilterのコンストラクタに渡すぼかし幅は大きめにした。そして,DisplayObject.cache()メソッドを呼出す矩形領域は,Bitmapインスタンスのイメージの領域となる。

function draw(eventObject) {

  blurBitmap = createBitmap(image, nX, nY);
  blurBitmap.filters = [new createjs.BoxBlurFilter(15, 15, 2)];
  blurBitmap.cache(0, 0, imageWidth, imageHeight);
  stage.update();
}

ぼかし幅をかなり大きくしたため,インスタンスのもとイメージがわからないくらいぼかされる図4⁠。script要素に書いたJavaScript全体は,つぎのコード2のとおりだ。

図4 インスタンスのイメージがかなりぼけた

図4 インスタンスのイメージがかなりぼけた

コード2 BoxBlurFilterクラスでインスタンスにぼかしフィルタをかける

var stage;
var blurBitmap;
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  var canvasSize = new createjs.Point(canvasElement.width, canvasElement.height);
  stage = new createjs.Stage(canvasElement);
  var loader = new createjs.LoadQueue(false);
  loader.addEventListener("fileload", draw);
  loader.loadFile({
    src: "images/image.png", 
    data: canvasSize
    });
}
function draw(eventObject) {
  var image = eventObject.result;
  var canvasSize = eventObject.item.data;
  var imageWidth = image.width;
  var imageHeight = image.height;
  var nX = (canvasSize.x - imageWidth) / 2;
  var nY = (canvasSize.y - imageHeight) / 2;
  blurBitmap = createBitmap(image, nX, nY);
  blurBitmap.filters = [new createjs.BoxBlurFilter(15, 15, 2)];
  blurBitmap.cache(0, 0, imageWidth, imageHeight);
  stage.update();
}
function createBitmap(image, nX, nY) {
  var myBitmap = new createjs.Bitmap(image);
  myBitmap.x = nX;
  myBitmap.y = nY;
  stage.addChild(myBitmap);
  return myBitmap;
}
※2
フィルタはEaselJSライブラリの基本的な機能という訳ではないので,JSファイルのサイズを抑えるためにコンパクト版からは外された(CreateJS SupportWhy are filters not included in the minified library?参照⁠⁠。

AlphaMaskFilterクラスでつくったアルファマスクをかける

つぎは,AlphaMaskFilterクラスを使う。インスタンスにアルファマスクをかける手順は,フィルタのお約束どおり3つだ。また,フィルタのクラスAlphaMaskFilterは,あらかじめscript要素に読込んでおく。

<script src="easeljs/filters/AlphaMaskFilter.js"></script>

もっとも,アルファマスクをかけるにはマスクが要る。そのため,マスク用のShapeインスタンスをつくる。そして,マスクする相手は,ぼかしたBitmapインスタンスではなく,新たにもうひとつオブジェクトを加える。そこで,コードにはつぎのような手直しをする。マスク用のShapeインスタンスと追加のBitmapオブジェクトは,それぞれ変数(wipingShapeとimageBitmap)に納めた。そして,Shapeインスタンスに描画する関数(wipe())を新たに定め,半透明(アルファ0.5)の塗りで円を描いている。

var imageBitmap;
var wipingShape;
var radius = 40;

function draw(eventObject) {

  wipingShape = new createjs.Shape();
  blurBitmap = createBitmap(image, nX, nY);
  blurBitmap.filters = [new createjs.BoxBlurFilter(15, 15, 2)];
  blurBitmap.cache(0, 0, imageWidth, imageHeight);
  imageBitmap = createBitmap(image, nX, nY);
  wipe();
}

function wipe(eventObject) {
  wipingShape.graphics
  .beginFill(createjs.Graphics.getRGB(0x0, 0.5))
  .drawCircle(50, 50, radius);

}

もう少し細かく見ていこう。Shapeインスタンスはアルファチャネルをマスクとして使うだけなので,ステージには置かない。そして,新たに加えたBitmapオブジェクトには,ぼかしたインスタンスと同じ画像イメージを与えた。しかも,後からStageオブジェクトの子として加えたため,ぼかしたインスタンスのうえに,もとのイメージが重ねられたことになる。

アルファマスクは,上に重ねたもとイメージのインスタンスにかける。すると,マスクのShapeオブジェクトは初め透明なので,上のBitmapインスタンスは見えなくなる。そこで,マスクのオブジェクトに半透明の円を描けば,上のインスタンスのその部分が半透明で表れるという段取りだ。

Shapeインスタンスに円を描く関数についても,少し補っておく。Shape.graphicsプロパティで描画の対象となるGraphicsオブジェクトが得られる。その参照を変数に入れて,Graphicsクラスのメソッドを呼び出すことにより,さまざまなベクターシェイプが描ける。第4回コード1は,この書き方でリングのシェイプをつくった。

var myGraphics = DisplayObjectオブジェクト.graphics;
myGraphics.Graphicsクラスのメソッド;
myGraphics.Graphicsクラスのメソッド;
…;

ところが,前掲コードではGraphicsオブジェクトを変数に入れず,そのまま続けてGraphics.beginFill()メソッドを呼び出した。実は,Graphicsクラスのメソッドは,基本的に呼出したGraphicsオブジェクトを返す。そのため,さらに続けてGraphics.drawCircle()メソッドが呼び出せる。このように,メソッドをいくらでも書連ねることができるのだ。

DisplayObjectオブジェクト.graphics.Graphicsクラスのメソッド.Graphicsクラスのメソッド.…;

ただ,1行で書くと何をやっているのか見にくいので,前掲のステートメントはドット(.)の前で改行している。なお,Graphics.getRGB()メソッドを用いると,カラーの整数とアルファのふたつの引数値でカラーを定める文字列が得られる。

アルファマスクをかける関数(updateCacheImage())は,つぎのように別に定めた。また,DisplayObject.cache()メソッドでフィルタの演算結果をキャッシュに描画する関数(updateCache())も分けた。引数にはブール(論理)値とキャッシュするインスタンスを渡す。

AlphaMaskFilter()コンストラクタは,マスクするイメージを引数にとる。具体的にはImageオブジェクトまたはCanvasで与える。つまり,Shapeオブジェクトはそのままではアルファマスクにはできない。そこで,DisplayObject.cache()メソッドを呼び出して(updateCache()⁠⁠,Canvasにキャッシュする。すると,その参照がShape.cacheCanvasプロパティで得られるので,それをAlphaMaskFilter()コンストラクタの引数に渡せばよい。

new AlphaMaskFilter(マスクイメージ)

あとは,フィルタオブジェクト(maskFilter)を配列に入れてDisplayObject.filtersプロパティに加え,別に定めた関数(updateCache())からDisplayObject.cache()メソッドでキャッシュするという手順どおりだ。なお,キャッシュする矩形領域の幅と高さはPointオブジェクトで変数(imageSize)に納めた。

var imageSize = new createjs.Point();

function draw(eventObject) {
  var image = eventObject.result;

  var imageWidth = imageSize.x = image.width;
  var imageHeight = imageSize.y = image.height;

}

function wipe(eventObject) {

  updateCacheImage(false);
}

function updateCacheImage(update) {
  updateCache(update, wipingShape);
  var maskFilter = new createjs.AlphaMaskFilter(wipingShape.cacheCanvas);
  imageBitmap.filters = [maskFilter];
  updateCache(update, imageBitmap);
  stage.update();
}
function updateCache(update, instance) {
  if (update) {

  } else {
    instance.cache(0, 0, imageSize.x, imageSize.y);
  }
}

これで,半透明のアルファマスクがShapeインスタンス(wipingShape)にもとづいてつくられ,AlphaMaskFilterクラスによりもとイメージをもつ前面のBitmapインスタンスに加えられる。その結果,半透明の円形の領域でぼかしが拭われたような表現になった図5⁠。

図5 半透明の円形でアルファマスクがかかってぼけが薄らぐ

図5 半透明の円形でアルファマスクがかかってぼけが薄らぐ

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書