POPSブログ

CreateJS MouseOverで背景を変化 ラベルを中心に配置した drawImage() で画像分割したボタン

251

  Category:  javascript2013/09/30 pops 

一般的な、MouseOver で色が変化するボタンの中に drawImage()で画像分割した画像を挿入しました。複数のアイコン画像などを1つのシート状にして読み込み分割して利用します。複数の画像を読み込む手間が省けます。
easeljs-0.7 (easeljs-0.7.0)でのテストです。

 

CreateJS MouseOver ラベルを中心に配置した drawImage() で画像分割したボタン


前ページのSpriteSheetボタンを drawImage()で画像分割に変えただけです。SpriteSheetボタンより少々処理が遅くなりますが、グラフイックに画像流し込み出来る分、応用性は良いかも知れません。
通常のBitmap画像として表示するもの、グラフイックの中に画像を流し込むもの、の2種類を作って見ました。 ボタン構造を変えるなどすれば少々ニュアンスの違うものも出来る。


2013/09/25/EaselJSなどバージョンUPされました(easeljs-0.7)。これより以後、easeljs-0.7 を使用します。2013/09/30
また、toDataURL()を使用しない方法に改めますので記事内容など変わりました。効率的に成っています。2013/10/01


処理は簡単ですが、多少問題がありますので説明が長くなります。


[説明図]

 

DEMO


CreateJS drawImage()で画像分割して、toDataURL()を使用しないでボタンを作るデモ、(createJS039.js)

このページはHTML5では有りませんので、デモページでご覧ください。「IE7.8」ではご覧いただけません。
/// 注意、ブラウザ、マシン性能により描画品質などに大きな差が有ります事了承下さい ///


Chrome Firefox Safari(Win) IE9、で動作確認済み。 Safari(Mac)、IE10、は未確認です。


画像変換に、toDataURL()を使用した場合の「デモ」と説明。面倒なため非推奨です。(createJS039b.js)


 

HTML JS CSS


使用するライブラリ

easeljs preloadjs tweenjs

配布元 : CreateJS createjs.com


ライブラリの読み込み

ダウンロードしたJSを使用する場合。記述は一例です。(バージョン 0.7.0 使用)


<script type="text/javascript" src="js/easeljs-0.7.0.min.js"></script>
<script type="text/javascript" src="js/tweenjs-0.5.0.min.js"></script>

重要、バージョン違いでは動かない場合が有りますので必ず合わせて下さい。
このデモでは preloadjs は不要です。


HTML (HTML5)


<div id="demo-wrap">
	<div id="image-box" class="radius"><canvas id="mainCanvas" width="640" height="300"></canvas></div>
</div>

JS

createJS039.js、JS名は任意に変更可。toDataURL()を使用しない新方式


//日本語
//createJS039.js
//新方式ボタンデモ用

//firefox判定
var userAgent = window.navigator.userAgent.toLowerCase();
var firefox=false;
if (userAgent.indexOf("firefox") > -1) {firefox=true;}

//------------------------------------------------------
//初期設定
//canvasの大きさ/全てこの値を使用
var canvasWidth=640;
var canvasHeight=300;
//------------------------------------------------------

//ステージ
var stage;

//ボタンコンテナ、インスタンス配列
var btncontainer;
var myhitBtn=[];
//ボタンコンテナ2、インスタンス配列2
var btncontainer2;
var myhitBtn2=[];

//icon画像Object
var iconSheet;

//画像分割URL容器
var srcArr=[];
var bitmaps=[];

//画像分割URL容器2必要だ、ブラウザ違い有り OperaはOK
var srcArr2=[];
var bitmaps2=[];

//shadowフィルター
var shadow=new createjs.Shadow("#000000",0,0,4);

//ステージ周りセット
function init() {

	//STAGE
	stage=new createjs.Stage('mainCanvas');
	//MouseOver設定/必要に応じ設定
	stage.enableMouseOver(20);

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

	//welcome画像層画像表示welcome_black.png
	var welcomeImage=new createjs.Bitmap('/main/images/welcome_back5.png');
	stage.addChild(welcomeImage);

	//角丸ボタンcontainer
	btncontainer=new createjs.Container();
	btncontainer.x=120;
	btncontainer.y=120;
	stage.addChild(btncontainer);
	//丸ボタンcontainer
	btncontainer2=new createjs.Container();
	btncontainer2.x=120;
	btncontainer2.y=220;
	stage.addChild(btncontainer2);

	//Ticker設定/画像分割は処理が遅いので必要
	createjs.Ticker.setFPS(30);
	createjs.Ticker.addEventListener('tick',tick);

	//ボタンを作り表示する
	iconSheet=new Image();
	iconSheet.onload=function() {

		//画像分割
		sliceBtns();

		//必要があれば調整する
		setTimeout(function() {

			//画像分割Circle
			sliceCircleBtns();

		},1000);
	};

	iconSheet.src="/main/images/icons01.png";
}

//------------------------------------------------------------------------------

//DRAW-sliceBtn
function sliceBtns() {

	var btn_width=84;//幅
	var btn_height=84;//高さ
	var c_radius=5;//コーナー半径
	var btn_spc=20;//ボタン間隔スペース
	var center=true;//全体の中央補正

	//スライスの大きさ
	var slice_width=80;
	var slice_height=80;

	var labels=["BUTTON1","BUTTON2","BUTTON3","BUTTON4","BUTTON5"];//ラベル
	var num=labels.length;//数

	//新方式画像の分割
	for (var i=0; i < num; i++) {

		var sliceCanvas=document.createElement("canvas");
		sliceCanvas.setAttribute("width",slice_width);
		sliceCanvas.setAttribute("height",slice_height);
		var context=sliceCanvas.getContext("2d");
		//貼り付け、横並びの場合
		context.drawImage(iconSheet,slice_width*i,0,slice_width,slice_height,0,0,slice_width,slice_height);
		//多少は時間がかかる
		bitmaps[i]=new createjs.Bitmap(sliceCanvas);

	}

	//マシン性能を考慮得し調整した方が良い
	setTimeout(function() {
		for (var i=0; i < num; i++) {

			//画像,sw,sh,x,y,w,h,r,c,label,size,lc,overc,outc,番号
			//RoundRect center補正ボタン/こちら使用
			myhitBtn[i]=createSliceBtn_center (bitmaps[i],slice_width,slice_height,-btn_width/2,-btn_height/2,btn_width,btn_height,c_radius,"#FFFFFF",labels[i],"12px","#FFFFFF","#48D1CC","#FFFFFF",i);

			//ラベルなし簡易ボタン/未使用
			//myhitBtn[i]=createSbtn (bitmaps[i],slice_width,slice_height,i);

			myhitBtn[i].cursor="pointer";
			myhitBtn[i].x=(btn_width+btn_spc)*i;
			myhitBtn[i].y=0;
			//myhitBtn[i].rotation=-15;//回転も可能
			//addChild
			btncontainer.addChild(myhitBtn[i]);

		}

		var btnbox_w=myhitBtn[num-1].x-myhitBtn[0].x;
		//中央配置補正
		if (center) {btncontainer.x=(canvasWidth-btnbox_w)/2;}

		//処理がおそいので Tickerが無いと表示出来ないことがある
		stage.update();

	},500);

}

//DRAW-sliceCircleBtn 処理が遅いので調整
function sliceCircleBtns() {

	var btn_width=84;//幅
	var btn_height=84;//高さ
	var c_radius=42;//円半径
	var btn_spc=20;//ボタン間隔スペース
	var center=true;//全体の中央補正

	//スライスの大きさ
	var slice_width=80;
	var slice_height=80;

	var labels=["BUTTON1","BUTTON2","BUTTON3","BUTTON4","BUTTON5"];//ラベル
	var num=labels.length;//数

	//新方式画像の分割
	for (var i=0; i < num; i++) {

		var sliceCanvas=document.createElement("canvas");
		sliceCanvas.setAttribute("width",slice_width);
		sliceCanvas.setAttribute("height",slice_height);
		var context=sliceCanvas.getContext("2d");
		//貼り付け、横並びの場合
		context.drawImage(iconSheet,slice_width*i,0,slice_width,slice_height,0,0,slice_width,slice_height);
		bitmaps2[i]=sliceCanvas;

	}

	//マシン性能を考慮得し調整した方が良い
	setTimeout(function() {

		for (var i=0; i < num; i++) {

			//画像,sw,sh,x,y,w,h,r,c,label,size,lc,overc,outc,番号
			//RoundRect center補正ボタン/こちら使用
			myhitBtn2[i]=createSliceCircleBtn_center (bitmaps2[i],slice_width,slice_height,0,0,btn_width,btn_height,c_radius,"#FFFFFF",labels[i],"12px","#FFFFFF","#48D1CC","#FFFFFF",i);

			//ラベルなし簡易ボタンCircle/未使用
 			//myhitBtn2[i]=createCbtn (bitmaps2[i],slice_width,slice_height,i);

			myhitBtn2[i].cursor="pointer";
			myhitBtn2[i].x=(btn_width+btn_spc)*i;
			myhitBtn2[i].y=0;
			//myhitBtn2[i].rotation=-15;//回転も可能
			//addChild
			btncontainer2.addChild(myhitBtn2[i]);

		}

		var btnbox_w=myhitBtn2[num-1].x-myhitBtn2[0].x;
		//中央配置補正
		if (center) {btncontainer2.x=(canvasWidth-btnbox_w)/2;}

		//処理がおそいので Tickerが無いと表示出来ないことがある
		stage.update();

	},500);
}

//------------------------------------------------------------------------------

//SliceBtn
//色cは未使用、outcを使用している
function createSliceBtn_center (bitmap_v,sw,sh,x,y,w,h,r,c,label,size,lc,overc,outc,no) {

	var font_v=size+" "+"Arial";

	//BTNコンテナ
	var btn=new createjs.Container();
	var s=new createjs.Shape();
	s.graphics.s().beginFill(outc);//本来は色cを使用
	operaRoundRect(s,x,y,w,h,r);
	//Shapeに陰影
	s.shadow=shadow;

	//Bitmap作成、再読み込みで遅い
	var bitmap=bitmap_v;
	bitmap.regX=sw/2;
	bitmap.regY=sh/2;

	btn.addChild(s,bitmap);
	//テキスト以外で使用は注意
	var tx=new createjs.Text(label,font_v,lc);
	tx.maxWidth=w;
	tx.textAlign="center";
	tx.textBaseline="middle";

	//firefox補正
	if (firefox) {tx.y=1;}
	//opera漢字補正用
	//if (window["opera"]) {tx.y=-1;}

	//ラベル位置を下げる
	tx.y=30;

	tx.shadow=shadow;//テキストは濃くなら無い
	tx.name="btntext";//name挿入
	btn.addChild(tx);

	//MouseOver
	btn.addEventListener("mouseover",function () {
		s.graphics.clear();
		s.graphics.s().beginFill(overc);
		operaRoundRect(s,x,y,w,h,r);
		stage.update();
	});
	//MouseOut
	btn.addEventListener("mouseout",function () {
		s.graphics.clear();
		s.graphics.s().beginFill(outc);
		operaRoundRect(s,x,y,w,h,r);
		stage.update();
	});
	return btn;
}

//CircleBtn
//色cは未使用、outcを使用している
function createSliceCircleBtn_center (img_obj,sw,sh,x,y,w,h,r,c,label,size,lc,overc,outc,no) {

	var font_v=size+" "+"Arial";

	//BTNコンテナ
	var btn=new createjs.Container();
	var s=new createjs.Shape();
	s.graphics.s().beginFill(outc).drawCircle(x,y,r);//本来は色cを使用
	//Shapeに陰影
	s.shadow=shadow;

	//Bitmapは事前に作る
	//Shape matrixで位置をずらす/幅swのみで計算
	var bmps=new createjs.Shape();
	var matrix=new createjs.Matrix2D();
	matrix.translate(-sw/2,-sw/2);

	//new Image()使用
	bmps.graphics.s().beginBitmapFill(img_obj,"no-repeat",matrix).drawCircle(x,y,sw/2);

	btn.addChild(s,bmps);
	//テキスト以外で使用は注意
	var tx=new createjs.Text(label,font_v,lc);
	tx.maxWidth=w;
	tx.textAlign="center";
	tx.textBaseline="middle";

	//firefox補正
	if (firefox) {tx.y=1;}
	//opera漢字補正用
	//if (window["opera"]) {tx.y=-1;}

	tx.shadow=shadow;//テキストは濃くなら無い
	tx.name="btntext";//name挿入
	btn.addChild(tx);

	//MouseOver
	btn.addEventListener("mouseover",function () {
		s.graphics.clear();
		s.graphics.s().beginFill(overc).drawCircle(x,y,r);
		stage.update();
	});
	//MouseOut
	btn.addEventListener("mouseout",function () {
		s.graphics.clear();
		s.graphics.s().beginFill(outc).drawCircle(x,y,r);
		stage.update();
	});
	return btn;
}


//------------------------------------------------------------------------------

//簡易ボタンBitmap
function createSbtn (bitmap_v,sw,sh,no) {

	//BTNコンテナ
	var btn=new createjs.Container();
	//Bitmap作成、再読み込みで遅い
	var bitmap=bitmap_v;
	bitmap.regX=sw/2;
	bitmap.regY=sh/2;
	//bitmapに陰影
	bitmap.shadow=shadow;
	btn.addChild(bitmap);
	return btn;
}
//簡易ボタンCircle
function createCbtn (img_obj,sw,sh,no) {

	//BTNコンテナ
	var btn=new createjs.Container();
	//Bitmapは事前に作る
	//Shape matrixで位置をずらす/幅swのみで計算
	var bmps=new createjs.Shape();
	var matrix=new createjs.Matrix2D();
	matrix.translate(-sw/2,-sw/2);
	//new Image()使用
	bmps.graphics.s().beginBitmapFill(img_obj,"no-repeat",matrix).drawCircle(0,0,sw/2);
	//Shapeに陰影
	bmps.shadow=shadow;
	btn.addChild(bmps);
	return btn;
}

//------------------------------------------------------------------------------

//tick
function tick() {
	stage.update();
}

//quadraticCurveTo
function operaRoundRect(s,x,y,w,h,r) {
	s.graphics.moveTo(x+r,y)
	.lineTo(x+w-r,y)
	.quadraticCurveTo(x+w,y,x+w,y+r)
	.lineTo(x+w,y+h-r)
	.quadraticCurveTo(x+w,y+h,x+w-r,y+h)
	.lineTo(x+r,y+h)
	.quadraticCurveTo(x,y+h,x,y+h-r)
	.lineTo(x,y+r)
	.quadraticCurveTo(x,y,x+r,y);
}

//START
init();

注釈文を削除すれば、幾分早くなります。
登録画像パスは当方の場合です。使用の際は環境に合わせて下さい。


CSS

createJS039.css、CSS名は任意に変更可


/*日本語 createJS039.css*/

#demo-wrap {
text-align:center;
}

#image-box {
position:relative;
top:0;left:0;
width:640px;
height:300px;
margin:0 auto;
padding:0;
box-shadow:0 0 10px #000;
-moz-box-shadow:0 0 10px #000;
-webkit-box-shadow:0 0 10px #000;
-o-box-shadow:0 0 10px #000;
-ms-box-shadow:0 0 10px #000;
background-color:#FFFFFF;
}

#image-box #mainCanvas {
border-radius:10px;
}
canvas {
border-style:none;
background-color:transparent;
}

.radius {
border-radius:10px;
-moz-border-radius:10px;
-webkit-border-radius:10px;
-o-box-border-radius:10px;
-ms-border-radius:10px;
}

当方のサンプルの例です。


簡単な説明


drawImage()画像分割ボタン

drawImage()画像分割に関しては、時折言及してきたので簡単にする。問題点などを列挙すると、、


1. drawImage()画像分割で使用する画像は必ず読み込み完了後に処理する
(但し、drawImage()はセキュリティ上、設置htmlと同じドメイン内の画像でなければ処理しません)
2. 画像分割してBitmap()クラスに変換してボタンの中に挿入する。(処理が遅いのでループの場合は注意)
3. ボタン構造の中に挿入しなくとも、単独でBitmap()クラスにクリックアクションを付ければボタンになる。
4. 注意点、マシン性能が悪い場合、特にChromeでは処理が遅くなるので、それを念頭に置いた処理をする。
5. グラフイックに画像流し込みは特殊だが、工夫すれば可能である。
6. 複数の処理が同時進行する時は、影響が大きいので工夫しなければならない。(マシン性能によりけり)
7. Android系で toDataURL() が処理出来ない情報が有りますが、当方では確認手段が無く不明です。
(当方ではまだ、一般ブラウザ以外の対応を考慮していません)


Bitmap()クラスに変換にtoDataURL()を使用した場合の問題点

Bitmap()クラス変換にtoDataURL()を使用した場合に処理が非常に遅くなります。よって他の方法を考えましたので変更します。

toDataURL()を使用した場合はこちらのデモページに説明など記載しました。「 toDataURL()を使用したデモページ
参考には成りますが、toDataURL()を使用した方法は破棄します。


Bitmap()クラス変換にtoDataURL()を使用しない簡単な方法

マタマタ問題があるので色々テストの結果簡単に drawImage() 画像分割後、toDataURL()を使用しないでBitmap()クラスに変換出来る方法をみつけました。toDataURL()にこだわり続けたのが問題だったようです。
(専門家の方法だったから、後、方法がないものと思っていた!、これで色々解決するものが多い!万歳)


1. toDataURL()を使用しない為に処理が効率的かつ早くなる。
2. toDataURL()の機能しないブラウザでも大丈夫と思います。(Android系) 但し未確認です。
3. 外部の画像でも画像分割して利用できる。


新たに分割するキャンバス要素を sliceCanvas として drawImage() 分解後、直接、Bitmap()に入れても可能です。


Bitmapにする

var sliceCanvas=document.createElement("canvas");
sliceCanvas.setAttribute("width",幅);
sliceCanvas.setAttribute("height",高さ);
var context=sliceCanvas.getContext("2d");
context.drawImage(元画像,X位置,Y位置,幅,高さ,0,0,幅,高さ);
var bitmap=new createjs.Bitmap(sliceCanvas);

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

グラフイックの利用も簡単だ、エライコッチャ

beginBitmapFill(sliceCanvas)

これでOKだ

これで色々な問題が片付くようです。直接、Bitmap()、beginBitmapFill() に入れられるので効率的だ。


但し、drawImage()がループ処理であれば以前として多少時間がかかるので、遅延させるようにした。マシン環境が良い場合は遅延させなくとも良いと思います。
問題であった、Chromeで、toDataURL()が遅くなるのも解消できる。


注意、var sliceCanvas=document.createElement("canvas") がループの中に入っている。


	//新方式画像の分割
	for (var i=0; i < num; i++) {

		var sliceCanvas=document.createElement("canvas");
		sliceCanvas.setAttribute("width",slice_width);
		sliceCanvas.setAttribute("height",slice_height);
		var context=sliceCanvas.getContext("2d");
		//貼り付け、横並びの場合
		context.drawImage(iconSheet,slice_width*i,0,slice_width,slice_height,0,0,slice_width,slice_height);
		//多少は時間がかかる
		bitmaps[i]=new createjs.Bitmap(sliceCanvas);

	}

	//マシン性能を考慮得し調整した方が良い
	setTimeout(function() {
		for (var i=0; i < num; i++) {

			略す

		}

		略す

	},500);

 

ラベルについて

画像ボタンですから、ラベルは特に必要とはしないかも知れませんが、一応表示出来るように構成しています。


ラベルを使用しない場合

ボタンの、ラベル(テキスト文字)を使用しない場合は、受け渡すラベルを「半角スペース」にして下さい。


var labels=["BUTTON1","BUTTON2","BUTTON3","BUTTON4","BUTTON5"];//ラベル

を「半角スペース」に変更
var labels=[" "," "," "," "," "];//ラベル

ラベルが何も無い(null)状態でも中心は変わらないようだが、ブラウザにより問題が出るか判らないので、「半角スペース」指定している。


ラベル位置の補正と調整

このボタンはテキストが、textAlign:center、textBaseline:middle、の設定で作られている構造です。
この「デモ」では画像が大きいために、ラベル(文字)に対してのマスクを設定していません。(もしマスクをした場合、デモではShape()に陰影処理しているためにマスク範囲が広がりますので注意下さい)


一応、Firefoxでラベル位置がずれるので補正していますが、ボタンが大きい場合は余り気にならないかとも思いますし、「ラベル無し」での使用も考えられますので、ラベル位置ズレは無視して良いかも知れません。
ボタン構造、ラベルなどの詳細は「前々ページ」を参照下さい。


● 補正: 画像の大きさにも左右されるが、余り問題なき場合は、高さの補正は無理にしなくとも良いと思う。


//firefox補正
if (firefox) {tx.y=1;}
//opera漢字補正用
//if (window["opera"]) {tx.y=-1;}

● 調整:テキストの位置は「y値」tx.y で調整可能です、ボタン全体の中心点は変わりません。


テキストの位置を中央より下に30ピクセル下げます

tx.y=30;

ラベル(テキスト)表示のズレなど

ブラウザにより、1-2ピクセル配置高さがズレル場合があります。画像が大きいので余り気になるものでは有りませんが、修正方法などは下記ページ参照下さい。

【参照】当方の記事: CreateJS ラベルを中心に配置し、MouseOver で背景を変化させるボタンを考える


陰影をつけただけの簡単なボタン

 

ラベルも、ボタン背景、MouseOverも無く、陰影を付けただけの簡単なボタンは下記の様に出来る。
一応、コンテナ収容で作り(コンテナ収容にしなくとも可能) createJS039.js に加えておくが未使用である。
コンテナ収容の形式ですから必要があればテキストなど書き加えることが出来ます。


MouseOverを使用しない場合
//stage.enableMouseOver(20);

-------------------------------------------------------
通常の処理の下にある設定のほうを有効にする

//ラベルなし簡易ボタン/角丸なし
myhitBtn[i]=createSbtn (bitmaps[i],slice_width,slice_height,i);

//ラベルなし簡易ボタン/Circle
myhitBtn2[i]=createCbtn (bitmaps2[i],slice_width,slice_height,i);

その外

その外の問題など、簡単に、、


種類の違うボタンなどを一括表示する場合

「デモ」の様に種類の違うボタンなどを一括表示する事はマレであると思います。分割と画像事前処理が沢山重なりますので、マシン性能によっては処理が遅くなる場合があります。一部の画像を表示しないなどの不具合が出る。
setTimeout()関数で旨く調整すると問題が解決します。(画像の大きさ、分割数によっても差はあるが、500-1000ミリ秒の遅延が必要)


マシン性能の良い環境下で、作ると見逃し易いので注意下さい。(グラフイックbeginBitmapFill()での処理は特に注意)


ボタンの改造などの注意

ボタンの中心位置決定の方法(textAlign:center、textBaseline:middle、の設定で作られている構造)などを理解の上に改造下さい。ややもするとgraphicsの中心がずれたりしますので、これらは自己責任で処理下さい。


Tickerの設定

テスト的には、Tickerを設定せずにstage.update()で表示出来る様に随所で遅延させていますが、Tickerを設定した方が良いと思います。
「デモ」では事前に Tickerを設定 を行っています。


使用画像

原則、使用目的に合わせて作ります。下記はサンプルで使用の画像です。

背景画像である、Welcome画像は別途用意下さい。ここでは、640x300サイズ


サンプル画像、icons01.png、400x80サイズ

 


SpriteSheet利用の画像ボタン、構造デザインは同じ。下記記事を参照下さい。

【参照】当方の記事: CreateJS MouseOverで背景を変化 ラベルを中心に配置した SpriteSheet 利用の画像ボタン

ラベルなどの詳細は、下記記事を参照下さい。

【参照】当方の記事: CreateJS ラベルを中心に配置し、MouseOver で背景を変化させるボタンを考える




一応、完動しますが全てテストです。効率化のため、予告無くJSなど修正する場合がありますので了承下さい。
また、CreateJSの「仕様」もクルクル変わっていますので、バージョン違い等に充分注意下さい


如何にゴマカスかがキーポイントなのだ!、以上です。

 


[ この記事のURL ]


 

ブログ記事一覧

年別アーカイブ一覧



[1]