POPSブログ

CreateJS 画像Bitmapの書き換え ステージのレイアウト を考える

212

  Category:  javascript2013/04/13 pops 

画像Bitmapの書き換えをするに、一般的なremoveChild addChild での画像交換(画像書き換え、画像更新)では無く直接画像Bitmapを流し込む方法を考えます。beginBitmapFill()についてもテストしてみました。easeljs-0.8用

 

CreateJS、で「ステージのレイアウトと画像Bitmapの書き換え」を考える


2014/12/13/EaselJSなどバージョンUPされました(easeljs-0.8)。easeljs-0.8用に更新しています。(2015/02/24)


CreateJSで処理して行くと問題になるのは「重ね順」のことです。簡単な構成ならば良いのですが、少しでも複雑であれば表示するパーツの「重ね順」が気になります。画像の切り替えでも「AS3」の様に直接画像インスタンスにBitmapを流し込む方法です。
以下、説明はバージョン easeljs-0.8 によるものです。(この辺は余りバージョンと関係ない)
以前の記事は少し判り難いかったので少し書き換えます。beginBitmapFill()に画像をいれた場合の振る舞いなども説明します。以下私の経験でわかった事柄です。


[ 目次 ]


 

 

ステージに階層構造を作る、画像の交換


 

ステージに階層構造を作る

▲[ 目次 ]

CreateJSでは、必ず最初にステージを作ることになります。簡単に画像1枚貼り付けるなどの単純な構成で「階層構造」は必要では有りませんが、色々なパーツ類で構成されている場合などは、Flashではレーヤー構造で重なりを区分。HTMLではDIV要素を重ねて作りますから問題はありません。(実際はFlashのレーヤーは重ね順を管理して、在るかのよう見せかけて居るだけらしいが!、FlashDevelopは無料だからレーヤー管理は出来ない)

パーツをステージに addChild() して行けばそれなりの構成は出来ますが、処理次第では途中で重なり順が変わったりします。勿論修正は出来ますが面倒です。addChild()は簡単だが少し不便な所も有ります。

CreateJS(Canvas)ではレーヤー構造が有りませんので、私の場合は少し重くなるが「コンテナ」を重ねて「レーヤーの様に構成」しています。このような考えの無い方には不必要なものです。目的により使う階層を固定し、全体の重なりを変化させない様に処理する考え方です。


階層構造の利点と欠点

1. 利点:重ね順の管理が仕易い、まとめて表示、非表示、移動など出来る。
2. 欠点:コンテナがある分重くなる、スクリプトが長くなる。作るのが面倒だ。
3. このような考えを強制するものでは有りません。


適当なCanvasがあると想定する。


<canvas id="myCanvas" width="640" height="300"></canvas>

● コンテナを利用した階層構造の例 (目的により使う階層を固定)


//グローバルにオブジェクトを作る、名前は随意
//canvasの大きさ
var canvasWidth=640;
var canvasHeight=300;
//
var canvasObject;
var stage;
//バック背景
var backrect;
//コンテナなど
var backcontainer;
var container;
var loadingcontainer;
var progresscontainer;
var textcontainer;
//インスタンス
var backImage;
var topImage;
var viewtext;

//initの実行
onload=init;

function init() {

	//ステージを作る
	stage=new createjs.Stage('myCanvas');

	//最下位色背景層、無くとも良い
	var backrect=new createjs.Shape();
	backrect.graphics.beginFill("#000000").drawRect(0,0,canvasWidth,canvasHeight);
	stage.addChild(backrect);

	//下画像層/空の画像挿入
	backcontainer=new createjs.Container();
	backImage=new createjs.Bitmap();
	backcontainer.addChild(backImage);
	stage.addChild(backcontainer);

	//上画像層/空の画像挿入
	container=new createjs.Container();
	topImage=new createjs.Bitmap();
	//必要なら中央補正
	container.x=canvasWidth/2;
	container.y=canvasHeight/2;
	container.addChild(topImage);
	stage.addChild(container);

	//Loading層
	loadingcontainer=new createjs.Container();
	stage.addChild(loadingcontainer);

	//ProgressBar層
	progresscontainer=new createjs.Container();
	stage.addChild(progresscontainer);

	//TEXT層
	textcontainer=new createjs.Container();
	stage.addChild(textcontainer);

	//Canvas更新表示する
	stage.update();

}

一例ですが、設計の状況次第では便利な場合もあります。当然スクリプトなどは増えます。個人的に使用しているもので、コンテナを作るのは面倒だ。これらの考えは制作される方の自由です。

コンテンツの設計次第では、コンテナを必要としない構造もあると思います。この辺は臨機応変に対応します。


画像の交換と操作

▲[ 目次 ]

スライドショウ的な画像の交換をする時に、removeChild addChild などで交換することも出来ます。
AS3の様に画像インスタンスを指定して直接Bitmapを流し込む手法で交換するならば、removeChild などと違い、重ね順の変化もなく効率的と思う。


直接画像Bitmapを交換する利点
まだ、BitmapDataクラスは存在しませんから画像Bitmapと表現するのは変ですが、今は直感的に理解してください。

1. インスタンス指定なので、コンテナなどを考慮しなくとも、ダイレクトに処理出来る。
2. 重なり順に変化が無い。メモリ効率も良い。


先に極一般的なremoveChild addChild で画像交換です。その後、直接画像Bitmapを流し込む方法を説明します。


removeChild addChild で画像交換 (非効率的)

▲[ 目次 ]

上の構造で下画像層、コンテナ(backcontainer) の中のBitmap()であるインスタンス(backImage)の画像を交換する事を考えて見ましょう。


● 説明例の構造
上の例の下画像層を例に説明する。先に空の画像(またはBitmap画像がある)を挿入しているとする。


//下画像層/空の画像挿入
backcontainer=new createjs.Container();
backImage=new createjs.Bitmap();
backcontainer.addChild(backImage);
stage.addChild(backcontainer);

画像データ無しのBitmap()はエラーにならない

● 記述例


mainImageは保存しているevent.resultの値

//一旦removeChildで削除
removeChild(backImage);
//同じインスタンスで新しい画像を作る
backImage=new createjs.Bitmap(mainImage);
//指定場所に画像をaddChild
backcontainer.addChild(backImage);

コンテナ(backcontainer) の中にあるので「重ね順」は変わらないが、中に複数の画像が在ったりしたら「重ね順」が変わったりするし非効率的である。

直ぐに削除する画像なら、簡単だから良いかも知れない。


画像result値

「画像result値」は次のもので、PNG画像と同等です。「画像」は「完全に読み込み完了」のものを使用するのが鉄則になります。尚、「クロスドメイン」画像は扱いが極めて困難ですので使用できません。


1. fileloadリスナーで得られた、event.result値。
2. Image()で得られたオブジェクト。
3. HTMLエレメント。(作り方は下参照)
4. 読み込み完了済み画像の、Bitmap().image。(result値と同等です)


● 具体的には下記の様にして得られた画像result値です。


var img;
------------------------------
//fileloadの event.result値

loader.on("fileload",fileload);

function fileload (event) {
	img=event.result;
}
------------------------------
//Image()で得られたオブジェクト

img=new Image();
img.onload=function(){
	//画像読み込み完了
};
img.src=画像URL
------------------------------
//HTMLエレメント画像(HTMLCanvasElement)

img=createColorCanvas(100,50,"#FFFFFF");

//色付きcanvasを作る
function createColorCanvas (w,h,c) {
	var canvas=document.createElement("canvas");
	canvas.width=w;
	canvas.height=h;
	var ctx=canvas.getContext("2d");
	ctx.fillStyle=c;
	ctx.fillRect(0,0,w,h);
	return canvas;
}
------------------------------
完全に読み込まれていれば使用できるが、非常に危険です
すぐ連続して処理を書くと正確に処理されない場合もある

//Bitmap(画像URL)で読み込まれた画像result値
img=new createjs.Bitmap(画像URL).image;

● ImageLoaderクラスで読み込む場合の注意。
読み込み後、加工処理などする場合は event を設定して event.result値 を使用ください。(easeljs-0.8)



var photo;
var bkloader=new createjs.ImageLoader("/main/images/testImage101.jpg",false);
bkloader.addEventListener("complete",function(event) {

	//result値の取り出し
	photo=event.result;

	処理を書く

});
bkloader.load();

--------------------------------------------------------

次の場合は画像処理が終了する前に次に進み、うまく処理されない場合が多い

	photo=new createjs.Bitmap("/main/images/testImage101.jpg").image;

	処理を書く


 

直接画像Bitmapを交換

▲[ 目次 ]

直接画像Bitmapを交換します。画像が「四角形」の場合に向いています。先にBitmap()で作られた画像インスタンスがあることが条件ですが、空画像であってもかまいません。下記の様に双方に .image を付けないと処理しませんので、注意が必要です。


● Bitmap().image の.image を交換します。
Bitmap()は空でも画像が存在してもエラーにはなりませんから何ら問題は有りません。




//空の.Bitmap()が存在する
var backImage=new createjs.Bitmap();
stage.addChild(backImage);

//Bitmapを流し込む
backImage.image=new createjs.Bitmap(画像result値).image;
stage.update();


● Bitmap画像のクリア

画像インスタンスの非表示で対処もできますが、画像のクリアも必要な場合もあります。以下の方法でクリアできます。インスタンス(Bitmap)は残っているので、以後再び画像の挿入は可能です。

ほとんど使用する機会は有りません。



//上画像に空Bitmap挿入、エラーにならない
var newimage=new createjs.Bitmap();
topImage.image=newimage.image;
stage.update();

//上画像に空Bitmap挿入、上と同じ
topImage.image=new createjs.Bitmap().image;
stage.update();

//直接nullにする
topImage.image=null;
stage.update();

 

グラフィックに画像を流し込んで交換 (beginBitmapFillを使用)

▲[ 目次 ]

グラフィックの場合はグラフィック要素の入ったShape()の構造であることが条件です。通常はボタンの画像切り替えなどに用いられると思う。イヤになるほど面倒ですネ。
Bitmap()と少々違いますので注意ください。
グラフィックをクリアしないと前の形が残る部分が有りますので、クリア実行後に交換します。


必ず、.drawXXXXX も記述しなければならないから面倒かも!。「保存result値」がなければ「エラー」になる。


● 書式、1

こちらの処理がマチガイが少ないのでお勧めです。処理は下記の様になります。img は画像result値です。


//対象画像インスタンス
var shape=new createjs.Shape();
stage.addChild(shape);

//.graphicsを直接描画する
shape.graphics.clear();
//shape.graphics.beginBitmapFill(保存result値).drawRect(大きさのデータ);
shape.graphics.beginBitmapFill(img).drawRect(0,0,100.50);
stage.update();

● 書式、2

new createjs.Graphics()で新たに作って代入しますので少し面倒です。
当初、new createjs.Graphics()の存在意義が判らなかったがこうすると良く判る。

必ず、new createjs.Graphics()、で新たに作ってから代入して下さい。



//対象画像インスタンス
var shape=new createjs.Shape();
stage.addChild(shape);

//新たにGraphics()オブジェクトを作り代入
g=new createjs.Graphics();
//g.beginBitmapFill(保存result値).drawRect(大きさのデータ);
g.beginBitmapFill(img).drawRect(0,0,100.50);

shape.graphics.clear();
shape.graphics=g;
stage.update();

● 説明例の構造
Bitmap()ではないので、Shape()に .graphics.beginFill().......のグラフィック形式であること。最初画像を入れるか、グラフィックにするかで書き方が違う。

面白いのは、初め空の beginFill().... を作っておき、あとで画像の beginBitmapFill(画像データ).... で画像を流し込める点です。空のグラフィックは必ず beginFill() で作ること。



//対象画像インスタンスの書き方こちらがマチガイが少ない
var shape=new createjs.Shape();
stage.addChild(shape);

//対象画像インスタンスの書き方こちらはマチガイ易い
var shape=new createjs.Shape();
shape.graphics.beginFill("#000000").drawRect(0,0,100.50);
stage.addChild(shape);

● 間違った代入法

Bitmap()画像の場合のような形式、次の様には成りません。エラーです。


shape.graphics=shape2.graphics;//エラー

注意、beginBitmapFill()、画像無しはエラーになる



注意、画像無しはエラーになる
backImage.graphics.beginBitmapFill().drawRect(0,0,canvasWidth,canvasHeight);
無駄な抵抗
backImage.graphics.beginBitmapFill("").drawRect(0,0,canvasWidth,canvasHeight);


● 代替画像で、beginBitmapFill()、のエラーを回避


var img=createColorCanvas(canvasWidth,canvasHeight,"#FFFFFF");
エラーを回避
backImage.graphics.beginBitmapFill(img).drawRect(0,0,canvasWidth,canvasHeight);

//色付きcanvasを作る
function createColorCanvas (w,h,c) {
	var canvas=document.createElement("canvas");
	canvas.width=w;
	canvas.height=h;
	var ctx=canvas.getContext("2d");
	ctx.fillStyle=c;
	ctx.fillRect(0,0,w,h);
	return canvas;
}

 

グラフィック処理関連の不具合など

▲[ 目次 ]

グラフィック処理に関する不具合と対処方法などを説明します。


ロールオーバー時に陰影が壊れる現象がある

グラフイックをロールオーバーなどのアクションで塗り替える場合に、陰影が壊れる現象が発生する場合がある。
Chromeと古いDisplay(古い液晶、ブラウン管形Display)の組み合わせの場合に、ロールオーバー毎に影が濃くなり壊れます。最近のDisplayなら発生しないでしょう。
これも、グラフイックの塗り替えの際に「クリア」すれば正常に陰影が描画されます。
更新され難い場合はupdate()を入れる。



btn.shadow=new createjs.Shadow("#000000",0,0,4);
//rollover
btn.addEventListener("rollover",function(){
	btn.graphics.clear();
	btn.graphics.beginFill("#FF0000").........
	stage.update();//必要であれば
});

● .beginBitmapFill()も.beginFill()も同じグラフイックです。

引数が空の場合に.beginBitmapFill()はエラーですが、.beginFill()は黒に染まるだけでエラーにはならない。
同じグラフイックですから、画像グラフイックに図形グラフイックを代入しても、逆でも描き変わるだけです。
一旦、クリアしなければ、上に重ね描きされます。


● グラフイックオブジェクトを保存する

オブジェクトを保存しておき使用するのも方法でしょうが、必ずクリアして塗り込んでいますので、オブジェクトの消失、延いては画像の消失に繋がる場合があります。グラフイック描画は結構早いですからその都度新規に作った方がベターです。
使用前にクローンを取り使用するならば、オブジェクトは消失しません。どうしても保存したいならば、グラフイックを画像にして保存することです。



最初にクリアするので、参照するオブジェクトが消失する恐れがある。
//クリア
shape.graphics.clear();
shape.graphics=保存されたオブジェクト;

● グラフイックを画像にする

グラフイックをキャッシュする事は出来ませんが、一旦コンテナに収容して、コンテナのキャッシュを得れば画像が得られます。原点の調整が必要な場合もあります。
GraphicsクラスにはgetCacheDataURL()メソッドは有りますが、.cacheCanvas は有りません。
Shapeクラスには.cacheCanvas は有ります。

また、コンテナに収容された全てを一括して画像に出来ることも強みです。


必要の場合があるかもしれない

var shape=new createjs.Shape();
shape.graphics.beginFill("#FF0000").drawRect(0,0,100.50);
//ステージにaddしない、メモリー上の処理
var container=new createjs.Container();
container.addChild(shape);
container.cache(0,0,100.50);
image=container.cacheCanvas;


shapeを画像にする
var shape=new createjs.Shape();
shape.graphics.beginFill("#FF0000").drawRect(0,0,100.50);
shape.cache(0,0,100.50);
image=shape.cacheCanvas;

コンテナの利便

コンテナに収納しておけば一括して処理管理出来る利便性がある。


//コンテナに収納
topImage=new createjs.Bitmap();
container.addChild(topImage);
stage.addChild(container);
---------------------------------
//コンテナ欄外に移動
container.x=カンバスの幅;
//コンテナ元に戻す
container.x=0;
//コンテナ非表示
container.visible=false;
---------------------------------
//指定のインスタンスを削除
removeChild(topImage);
//指定の親インスタンスの中の子インスタンスを全て削除
container.removeAllChildren();
//コンテナごと削除すれば、コンテナと中の画像もなくなる
removeChild(container);

● beginBitmapFill()に挿入出来る画像データ

beginBitmapFill()に挿入出来る画像データは、上記説明の「読み込み完了」の画像result値のみです。
画像が無ければ重大エラーに繋がります。
問題があれば、代替画像としてHTMLエレメント画像を挿入すれば安心です、作り方は上記参照。


● グラフィックの削除

グラフィックは addChild で操作されてはいない、グラフィックだけ削除するならば、下記で出来るようだ。
ここでの backImage はグラフィックを内包するShape()です。使う機会があるか疑問だ。


backImage.graphics=null;

 

ChromeのbeginBitmapFill()とMatrix処理の不具合


zu

 

上図はChromeでのMatrix処理でスケールを変えた場合の不具合を表します。おそらくPNGの違いに拠るものと思いますが、Chromeが修正する事はないと思います。
(PNG画像に変換して描画されている訳ですが、ChromeではPNG処理の種類が違います透明度情報を持ったPNGと言う事ですが...)
Matrix処理でスケール処理をした場合で、角丸陰影処理すると図の様に壊れる。修正方法を考えます。


1. ChromeでbeginBitmapFill()で画像をいれ、角丸陰影処理をすると、完全に壊れます。
2. drawRoundRect()では正常です。(不思議なところ)
3. beginBitmapFill()にクロスドメイン画像は入れない事。(地獄を見ます)


対処方法 1

ChromeでのMatrix処理でスケールを変えた場合の不具合はMatrixを使用せずに、drawImage()でスケール変更したcanvasエレメントを入れれば直る。(Matrixを変えてみたり、あらゆる修正を試みたが全てダメであった)


次のMatrix処理に陰影処理で壊れる。


photoは画像result値とする

var shape=new createjs.Shape();
//Matrix縮小75%/Chrome陰影切れる
var scale_x=0.75;//scaleX
var scale_y=0.75;//scaleY
var mtx=new createjs.Matrix2D();
mtx.scale(scale_x,scale_y);
shape.graphics.beginBitmapFill(photo,"no-repeat",mtx).drawRoundRect(0,0,75,75,10);
shape.x=50;
shape.y=50;
//陰影が壊れる
shape.shadow=new createjs.Shadow("#000000",0,0,10);
stage.addChild(shape);

画像を、HTML5カンバスメソッド、.drawImage()でスケール変更する。
.drawImage()でスケール変更するとずれません。大きさは整数値である事


var photo_v=createImgCanvas(75,75,photo);
shape.graphics.beginBitmapFill(photo_v).drawRoundRect(0,0,75,75,10);
//陰影は正常
shape.shadow=new createjs.Shadow("#000000",0,0,10);

-----------------------------------
//幅、高さ、画像
function createImgCanvas(w,h,patternimg) {
	//Box
	var canvas=document.createElement("canvas");
	//指定の大きさになる
	canvas.width=w;
	canvas.height=h;
	var ctx=canvas.getContext("2d");
	ctx.drawImage(patternimg,0,0,w,h);

	return canvas;
}

対処方法 2

陰影を付けるインスタンスを変えれば良い。
shape.graphics.beginBitmapFill()の下に同じグラフイックを配置して、そこに陰影処理をする。


shape.graphics.beginBitmapFill(photo,"no-repeat",mtx).drawRoundRect(0,0,75,75,10);
var base=new createjs.Shape();
base.graphics.beginBitmapFill("#000000").drawRoundRect(0,0,75,75,10);
//陰影は別の場所
base.shadow=new createjs.Shadow("#000000",0,0,10);
stage.addChild(base,shape);

対処方法 3

コンテナでラップしてコンテナに陰影処理する。但しブラウザによっては要らないい部分に陰影が付いたりして同一には成らない欠点がある。確認して良いと思えば問題は無い。



var shape_container=new createjs.Container();
shape.graphics.beginBitmapFill(photo,"no-repeat",mtx).drawRoundRect(0,0,75,75,10);
//陰影はコンテナに
shape_container.Shadow("#000000",0,0,10);
shape_container.addChild(shape);
stage.addChild(shape_container);

対処方法 4

些細なことは気に留めないで放っておく。


 

 

動作サンプルは下記記事などを参照クダサイ。

【参照】当方の記事: CreateJS 画像クロスフェード、一括画像読み込み、タイマー形式

【参照】当方の記事: CreateJS easeljs-0.8のLoadQueueクラスでの画像表示の変更など


面倒きわまり無い。適当とゴマカシで乗り切りましょう。
以上です。

 


[ この記事のURL ]


 

ブログ記事一覧



[1]