POPSブログ

CreateJS 図形テキストをアニメーション、Tween 連続操作と MotionGuideTween の組み合わせ

238

  Category:  javascript2013/07/30 pops 

CreateJSを利用してキャンバスに、図形サークルとテキストをアニメーションで表示します。「要素」毎のアニメと「全体」のアニメとの複合形式で「集合拡散」を連続して表現します。MotionGuidePlugin に拠るガイドに沿ってTweenする機能も組み合わせてみました。
easeljs-0.7 でのテストです。

 

CreateJS 図形テキストをアニメーション、Tween 連続操作と MotionGuideTween の組み合わせテスト


2013/09/25/EaselJSなどバージョンUPされました(easeljs-0.7)。動作など確認してeaseljs-0.7用に更新しています。(2013/10/22)


図形テキストをアニメーションにMotionGuidePlugin に拠るガイドに沿ってTweenする機能も組み合わせてみましたので、曲線によるTweenが可能ですが少々複雑です。
ブラウザにより「回転精度」「描画品質」が違いますので注意下さい。

 

[説明図]

サンプルには下記の形を用意しました。Circle. Star. Rect. RoundRect. Line. Leaf. Ellipse.

 

DEMO


CreateJS 図形テキストをアニメ、連続操作とMotionGuideの組合せ (createJS028.js)

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


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


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

createJS028.js、JS名は任意に変更可。easeljs-0.7用


//日本語
//createJS028.js
//V0.7用MotionGuideアニメーション
//rainbow-Color

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

//Plugin読み込み
createjs.MotionGuidePlugin.install();

//メインテキスト
var mainstring="ViVa POPS WEB KOUBOU";
//サブテキスト
var substring="Hohhokekyo! Welcome to my site. thanks";
//canvasの大きさ/全てこの値を使用
var canvasWidth=640;
var canvasHeight=300;

//circle
var slice_value=24;//分割数
var base_angle=-90;//ベース角度補正
var range=360;//配置範囲レンジ角度 180 240 360

//図形インスタンス番号
//Circle=0 Star=1 Rect=2 RoundRect=3 Line=4 Leaf=5 Ellipse=6
var type_no=0;

//------------------------------------------------------
//変数、未使用もあり

//ステージ
var stage;
//box
var backrect;
var welcomecontainer;
var circlecontainer;
var pathcontainer;
//パネル
var welcomePanel;
//TEXT
var viewtext;

//CircleBase
var circleBase;
//保存
var ballChip=[];
var ballPosx=[];
var ballPosy=[];

//MAIN-TEXT
var maintextcontainer;
var maintext;
//MOVE-TEXT
var textChip=[];
var textPosx=[];
var textPosy=[];
//MOVEポジション保存
var movePosx=[];
var movePosy=[];
//スケール保存
var keepScale=0;
//回転保存
var keepRote=0;

//SUB-TEXT
var subtextcontainer;
var subText;

//------------------------------------------------------
//shadowフィルター
var shadow=new createjs.Shadow("#000000",0,0,4);
var shadow2=new createjs.Shadow("#FFFFFF",0,0,6);//影2

var backchg_no=0;
var backcolors=["#000000","#FFFFFF","#778899","#BC8F8F","#20B2AA"];

//サークル用グローバル変数
var partscount=0;
//テキスト用グローバル変数
var textmax=0;
var textcount=0;
//全て終了グローバル変数
var allcount=0;

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

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

	//STAGE
	stage=new createjs.Stage('mainCanvas');

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

	//クリック
	backrect.addEventListener("click",backchg);
	stage.addChild(backrect);

	//welcome画像層パネル表示
	welcomecontainer=new createjs.Container();
	welcomecontainer.x=canvasWidth/2;
	welcomecontainer.y=canvasHeight/2;
	//welcome-CirclePanel
	//x y 半径 色 文字サイズ 文字色 文字列
	welcomePanel=createCirclePanel(0,0,95,"#4A4A4A","italic bold 20px","#FFFFFF","WELCOME");
	welcomePanel.shadow=shadow;
	welcomecontainer.addChild(welcomePanel);
	stage.addChild(welcomecontainer);
	welcomePanel.visible=false;

	//circle-Text circlecontainer
	circlecontainer=new createjs.Container();
	circlecontainer.x=canvasWidth/2;
	circlecontainer.y=canvasHeight/2;
	//ベース
	circleBase=new createjs.Container();
	circlecontainer.addChild(circleBase);
	stage.addChild(circlecontainer);

	//SUB-TEXT
	subtextcontainer=new createjs.Container();
	//位置中央
	subtextcontainer.x=canvasWidth/2;
	subtextcontainer.y=canvasHeight/2+60;
	stage.addChild(subtextcontainer);

	//MAIN-TEXT
	maintextcontainer=new createjs.Container();
	//位置中央/あとで修正される
	maintextcontainer.x=canvasWidth/2;
	maintextcontainer.y=canvasHeight/2;
	stage.addChild(maintextcontainer);

	//簡易TEXT、Arialに変更
	viewtext=new createjs.Text("","12px Arial","#FFFFFF");
	viewtext.x=20;
	viewtext.y=15;
	viewtext.maxWidth=canvasWidth-40;
	viewtext.lineHeight=20;
	viewtext.textBaseline="bottom";
	viewtext.shadow=shadow;//SHADOW処理
	stage.addChild(viewtext);
	set_text("START");

	//パス描画コンテナ、描画動作が確認できれば要らない
	pathcontainer=new createjs.Container();
	pathcontainer.x=0;
	pathcontainer.y=150;
	stage.addChild(pathcontainer);
	pathcontainer.visible=false;

	stage.update();

	createjs.Ticker.setFPS(30);
	createjs.Ticker.addEventListener('tick',tick);

	//調整
	setTimeout(function() {

		//プロローグ表示に進む
		set_prologue();

	},2000);

}

//プロローグ
function set_prologue() {

	//全て終了グローバル変数
	allcount=0;//クリア
	set_text("");
	//circle
	set_circleparts();

	//調整、文字は少し遅れて描画
	setTimeout(function() {

		//MAIN-TEXT
		set_mainanime();
		//SUB-TEXT非表示
		set_subtext();

	},2000);

}

//ガイドのパス描画、確認したのでお役目御免
function draw_line() {
	var reverse_v=1;
	var guide_w=canvasWidth;
	var path = new createjs.Shape();
	path.graphics.beginStroke("#ff0000").moveTo(guide_w,0);
	path.graphics.curveTo(guide_w*.75,guide_w/2*.75*reverse_v,guide_w/2,0);
	path.graphics.curveTo(guide_w*.25,-guide_w/2*.75*reverse_v,0,0);
	pathcontainer.addChild(path);
}
function draw_line2() {
	var radius=canvasHeight/2*.8;//サークル半径
	var path2 = new createjs.Shape();
	path2.graphics.beginStroke("#ff00ff").moveTo(radius,-radius*2).curveTo(-radius,-radius,0,0);
	pathcontainer.addChild(path2);
}

//図形サークルアニメ
function set_circleparts() {

	ballChip=[];//クリア
	partscount=0;//カウント、グローバル

	var radius=canvasHeight/2*.8;//サークル半径
	//ベース
	circleBase=new createjs.Container();
	circlecontainer.addChild(circleBase);
	//ベース角度補正
	circleBase.rotation=base_angle;

	text_W=0;//全幅未使用

	//インスタンス配列取得
	for (var i=0; i < slice_value; i++) {

		//Circleインスタンス
		if (type_no == 0) {
			//(x,y,r,color,rainbow,len,no)
			ballChip[i]=createBall(0,0,10,"#CCCCCC",true,slice_value,i);
		}
		//Starインスタンス
		if (type_no == 1) {
			//(x,y,r,color,rainbow,len,no)
			ballChip[i]=createStar(0,0,6,"#CCCCCC",true,slice_value,i);
		}
		//Rectインスタンス
		if (type_no == 2) {
			//(x,y,w,h,color,rainbow,len,no)
			ballChip[i]=createRect(0,0,20,20,"#CCCCCC",true,slice_value,i);
		}
		//RoundRectインスタンス
		if (type_no == 3) {
			//(x,y,w,h,r,color,rainbow,len,no)
			ballChip[i]=createRoundRect(0,0,20,20,4,"#CCCCCC",true,slice_value,i);
		}
		//Lineインスタンス
		if (type_no == 4) {
			//(x,y,w,h,color,rainbow,len,no)
			ballChip[i]=createLine(0,0,10,20,"#CCCCCC",true,slice_value,i);
		}
		//Leafインスタンス
		if (type_no == 5) {
			//(x,y,w,h,color,rainbow,len,no)
			ballChip[i]=createLeaf(0,0,20,20,"#CCCCCC",true,slice_value,i);
		}
		//Ellipseインスタンス
		if (type_no == 6) {
			//(x,y,w,h,color,rainbow,len,no)
			ballChip[i]=createEllipse(0,0,10,20,"#CCCCCC",true,slice_value,i);
		}

		//ポジション取得文字用に残す、未使用
		ballPosx[i]=0;
		ballPosy[i]=0;
	}

	//間隔の基準時間
	var base_time=150;
	//交互遅延時間 500-1000
	var delayed_time=1000;

	//delay
	var delay_st=[];
	var delay_st2=[];

	//パターン番号
	var pattern_no=Math.floor(Math.random()*5);

	//多目的判定用フラグ
	var chgxyflag=Math.floor(Math.random()*5);
	//多目的用2
	var chgflag=Math.floor(Math.random()*3);
	var chgflag2=Math.floor(Math.random()*3);
	var chgflag3=Math.floor(Math.random()*3);//ガイド用
	//逆転フラグ
	var chg_v=Math.floor(Math.random()*2);
	if (chg_v < 1) {chg_v=-1;}

	//Guideパターン番号2
	var guidepattern_no=Math.floor(Math.random()*3);;
	//シャッフル
	var shuffle=false;
	if (guidepattern_no == 2) {shuffle=true;}
	//ガイド反転reverse
	var reverse_v=1;
	if (chgflag3 == 1) {reverse_v=-1;}

	var k=0;
	var flag=-1;
	var angle=0;
	var textwrap=[];

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

		angle =i*360/slice_value*range/360;//range展開角度1、0度より
		//angle +=360/slice_value*range/360;//range展開角度2、ズレ

		//WRAP
		var wrap=new createjs.Container();
		wrap.regX=0;
		wrap.regY=radius;
		wrap.x=0;
		wrap.y=0;
		wrap.rotation=angle;//angle;
		ballChip[i].x=0;
		ballChip[i].y=0;

		//移動
		ballChip[i].x=radius;
		ballChip[i].y=-radius*2;

		//識別ID登録
		ballChip[i].id=i;

		ballChip[i].scaleX=0;//0
		ballChip[i].scaleY=0;//0
		ballChip[i].rotation=60*reverse_v;//ガイド反転
		ballChip[i].alpha=1;//1

		//addChild
		wrap.addChild(ballChip[i]);
		textwrap[i]=wrap;
		//addChild
		circleBase.addChild(textwrap[i]);

		k ++;
		flag *=-1;
	}

	//delay計算、delay逆転が効かない
	var k=0;
	var flag=-1;
	var sh=slice_value-1;
	var kk=0;
	for (var i=0; i < slice_value; i++) {
		kk=sh-i;

		//反対
		if (pattern_no == 0) {
			delay_st[i]=base_time*kk;
		}
		//中から
		if (pattern_no == 1) {
			delay_st[i]=Math.abs(i-sh/2)*base_time*1.5;
		}
		//外から
		if (pattern_no == 2) {
			delay_st[i]=i*base_time*1.5;
			if (i > sh/2) {delay_st[i]=kk*base_time*1.5;}
		}
		//正
		if (pattern_no > 2) {
			delay_st[i]=base_time*i;
		}
		//交互遅延 delayed_time
		if (chgflag == 1) {
			if (flag > 0) {
				delay_st[i] +=delayed_time;
			}
		}

		//Guideパターン同時
		delay_st2[i]=0;
		//交互遅延
		if (guidepattern_no == 1) {
			if (flag > 0) {
				delay_st2[i] +=delayed_time;
			}
		}
		//シャッフル
		if (guidepattern_no == 2) {
			delay_st2[i]=i*base_time*1.5;
		}

		k ++;
		flag *=-1;
	}

	//delayシャッフル実行
	if (shuffle) {
		var num=delay_st2.length;
		for(var i=0;i < num;i++){
			var r=Math.floor(Math.random()*num);
			var t=delay_st2[r];
			delay_st2[r]=delay_st2[i];
			delay_st2[i]=t;
		}
	}

	//delayの最大値を求める
	var max_delay=Math.max.apply(null,delay_st);
	var max_delay2=Math.max.apply(null,delay_st2);

	//収縮ポイント移動
	var radius_y=radius*(chgflag2-1);
	var scale_end=(chgflag-1)*2;

	//Tween
	for (var i=0; i < slice_value; i++) {

		//Tween2
		var twn2=createjs.Tween.get(ballChip[i])
		.wait(delay_st2[i])//Guide
		.to({guide:{path:[radius*reverse_v,-radius*2, -radius*reverse_v,-radius,0,0],orient:false},rotation:-45*reverse_v,scaleX:1,scaleY:1,alpha:1},2200)//ガイド反転
		.to({x:0,y:-5,scaleX:1.5,scaleY:1.5},100)
		.to({x:0,y:0,scaleX:1,scaleY:1},500)

		.wait(max_delay2-delay_st2[i])//最大値利用時間差吸収
		.wait(1000)
		.to({x:0,y:-50,scaleX:3,scaleY:3,alpha:.5},400)
		.wait(1000)
		.to({x:0,y:80,scaleX:.5,scaleY:.5,rotation:360*chg_v},2000)
		.wait(500)
		.to({x:0,y:0,scaleX:1,scaleY:1,rotation:0,alpha:1},1000)
		.wait(1000)

		.wait(delay_st[i])
		.to({x:0,y:0,rotation:0},400)
		.to({x:0,y:0,scaleX:1,scaleY:1,rotation:215,alpha:1},400)

		.to({x:-100,y:radius_y*2.5,scaleX:2,scaleY:2,rotation:45,alpha:.5},800)
		.wait(max_delay)//展開するまで遅延
		.to({x:0,y:0,scaleX:1,scaleY:1,rotation:-225,alpha:1},600)

		.wait(max_delay-delay_st[i])//最大値利用時間差吸収
		.to({x:0,y:-50,scaleX:1.5,scaleY:1.5,rotation:0,alpha:.5},800)
		.wait(600)
		.to({x:0,y:0,scaleX:1,scaleY:1,rotation:0,alpha:1},600)
		.wait(8000)
		.to({x:0,y:10*(chgflag2-1),scaleX:-2,scaleY:-2,rotation:315*chg_v,alpha:.5},1000)
		.wait(1000)
		.to({x:0,y:0,scaleX:1,scaleY:1,rotation:0,alpha:1},800)
		.wait(1000)
		.to({x:0,y:canvasWidth/2*(chgflag-1),scaleX:scale_end,scaleY:scale_end,rotation:0,alpha:0},1800)
		.call(function(){
			//終了
			if (partscount == slice_value-1) {finshAll();}
			partscount +=1;//グローバルの事
		});
	}

}

//MAINテキストアニメ
function set_mainanime() {

	textcount=0;//カウント、グローバル

	textChip=[];//クリア
	//MAINテキストアニメーション
	var maintext_W=0;
	var text_spc=0;//テキストスペース
	var center=true;
	var tcount=0;

	//インスタンス配列取得
	//サイズ,フォント,色,虹色,スペース,文字
	textChip=createMoveText("40px","Arial","#FFFFFF",true,text_spc,mainstring);
	var textlen=mainstring.length;
	//テキスト用グローバル変数
	textmax=textlen;

	//ランダム配置のため全幅の計算を先に
	for (var i=0; i < textlen; i++) {
		maintext_W +=textChip[i].width;
		if(i < textlen) {maintext_W +=text_spc;}
	}
	//コンテナ中央補正
	if (center) {maintextcontainer.x=(canvasWidth-maintext_W)/2;}

	//guideガイド変数
	var guidePos_x=[];
	var guide_w=canvasWidth;//ガイド幅canvasWidth

	//間隔の基準時間
	var base_time=150;//100-200
	//交互遅延時間 500-1000
	var delayed_time=1000;

	//パターン9指定ポイント
	//maintextcontainer原点基準
	var start_x=maintext_W/2;
	var start_y=-100;

	//移動
	var delay_st=[];
	//パターン番号
	var pattern_no=Math.floor(Math.random()*10);

	//多目的判定用フラグ
	var chgxyflag=Math.floor(Math.random()*5);
	//多目的用2
	var chgflag=Math.floor(Math.random()*3);
	var chgflag2=Math.floor(Math.random()*3);
	var chgflag3=Math.floor(Math.random()*3);//ガイド用
	//逆転フラグ
	var chg_v=Math.floor(Math.random()*2);
	if (chg_v < 1) {chg_v=-1;}

	//回転
	var rotate_no=chgflag*chg_v;
	//スケール
	var sclsdata=[1,1,1,0,.5,4,8,12];
	var scls=sclsdata[Math.floor(Math.random()*sclsdata.length)];

	//delay番号
	var delay_no=Math.floor(Math.random()*4);

	//ガイドreverse反転/mutual交互
	var mutual=false;
	var reverse_v=1;
	if (chgflag3 == 1) {reverse_v=-1;}//反転
	if (chgflag3 == 2) {mutual=true;reverse_v=1;}//交互
	//半径
	var radius=canvasHeight/2;
	//alphaの調整
	var alpha_no=0;
	if (scls < 0.2) {alpha_no=1;}

	var sh=textlen-1;
	var k=0;
	var flag=-1;

	for (var i=0; i < textlen; i++) {
		//ID
		textChip[i].id=i;
		//addChild
		maintextcontainer.addChild(textChip[i]);
		//ポジション取得
		textPosx[i]=textChip[i].x;
		textPosy[i]=textChip[i].y;

		//移動左右1、X方向のみ
		if (pattern_no == 0) {
			textChip[i].x +=canvasWidth*chg_v;
			textChip[i].y=textPosy[i];
		}
		//移動左右2、Y方向ズレ
		if (pattern_no == 1) {
			textChip[i].x +=canvasWidth/2*chg_v;
			textChip[i].y +=100*flag*chg_v*(chgflag2-1);
		}
		//移動上下
		if (pattern_no == 2) {
			textChip[i].x=textPosx[i];//その場
			textChip[i].y=canvasHeight/2*chg_v*(chgflag+1);
			if (chgflag == 1) {
				textChip[i].y *=flag;//上下交互
			}
		}
		//ランダム/Global位置から
		if (pattern_no == 3) {
			//globalToLocal
			var point=maintextcontainer.globalToLocal(Math.random()*canvasWidth,Math.random()*canvasHeight);
			textChip[i].x=point.x;
			textChip[i].y=point.y;
		}
		//文字列の中心から
		if (pattern_no == 4) {
			textChip[i].x=maintext_W/2;
			textChip[i].y=textPosy[i];//その場
		}
		//その場所から
		if (pattern_no == 5) {
			textChip[i].x=textPosx[i]+20*(chgflag-1);//その場少しずらす
			textChip[i].y=textPosy[i];//その場
		}
		//文字中央2分割して左右に
		if (pattern_no == 6) {
			if (i < textlen/2) {textChip[i].x -=canvasWidth/2*chg_v;}
			if (i >= textlen/2) {textChip[i].x +=canvasWidth/2*chg_v;}
			textChip[i].y=textPosy[i];//その場
		}
		//中央に集結、文字中央から半径radius
		if (pattern_no == 7) {
			var angle=360*i/textlen;
			textChip[i].x=maintext_W/2+Math.cos(angle)*radius;
			textChip[i].y=textPosy[i]+Math.sin(angle)*radius;
		}

		//ガイド左右用
		if (pattern_no == 8) {
			//移動
			guidePos_x[i]=textPosx[i]+guide_w*chg_v;
			textChip[i].x=guidePos_x[i];
			textChip[i].y=textPosy[i];
		}

		//指定ポイントから
		if (pattern_no == 9) {
			textChip[i].x=start_x;
			textChip[i].y=start_y;
			scls=0;
		}

		//MOVEポジション保存
		movePosx[i]=textChip[i].x;
		movePosy[i]=textChip[i].y;
		//スケール保存
		keepScale=scls;
		//回転保存
		keepRote=rotate_no*180*(chgflag2-1);
		if (pattern_no == 8) {
			keepRote *=chg_v;//逆転あり
		}
		//セット
		textChip[i].rotation=keepRote;
		textChip[i].alpha=alpha_no;//調整
		textChip[i].scaleX=scls;
		textChip[i].scaleY=scls;

		//共通delay
		delay_st[i]=base_time*i;//標準
		if (delay_no == 0) {delay_st[i]=80*i;}//速く
		if (delay_no == 1) {delay_st[i]=Math.abs(i-sh/2)*base_time;}//中から
		if (delay_no == 2) {delay_st[i]=Math.abs(Math.abs(i-sh/2)-textlen/2)*base_time;}//外から

		//ガイド少し遅くに修正
		if (pattern_no == 8) {
			delay_st[i] *=1.5;
		}

		//交互遅延
		if (chgflag2 == 1) {
			if (flag == 1) {delay_st[i] +=delayed_time;}
		}

		k ++;
		flag *=-1;
	}

	//delay逆転/全て機能しない
	if (chgflag == 0) {
		//delay_st.reverse();
	}

	//delayの最大値を求める
	var max_delay=Math.max.apply(null,delay_st);

	//OUT時、ガイドと他の速度を違わせる
	var speed_v=1000;//800-1000
	if (pattern_no == 8) {speed_v=1500;}

	var flag=-1;
	//Tween
	for (var i=0; i < textlen; i++) {

		if (mutual) {reverse_v *=flag;}//ガイド波形交互
		if (pattern_no == 7) {pos_x=maintext_W/2;pos_y=0;}

		var params;
		if (pattern_no != 8) {params={x:movePosx[i],y:movePosy[i],scaleX:keepScale,scaleY:keepScale,alpha:0,rotation:keepRote};}
		if (pattern_no == 8) {params={guide:{path:[movePosx[i],movePosy[i], guide_w*.75*chg_v+textPosx[i],guide_w/2*.75*reverse_v,guide_w/2*chg_v+textPosx[i],0, guide_w*.25*chg_v+textPosx[i],-guide_w/2*.75*reverse_v,textPosx[i],textPosy[i]],orient:false,start:1,end:0},scaleX:keepScale,scaleY:keepScale,alpha:alpha_no,rotation:keepRote};}//左右用

		//Tween
		var twn=createjs.Tween.get(textChip[i]);
		if (pattern_no == 8) {
			twn.wait(delay_st[i])
			.to({guide:{path:[movePosx[i],movePosy[i], guide_w*.75*chg_v+textPosx[i],guide_w/2*.75*reverse_v,guide_w/2*chg_v+textPosx[i],0, guide_w*.25*chg_v+textPosx[i],-guide_w/2*.75*reverse_v,textPosx[i],textPosy[i]],orient:false},rotation:0,scaleX:1,scaleY:1,alpha:1},2000)//左右用
			.to({x:textPosx[i],y:textPosy[i],rotation:0},50);
		}
		if (pattern_no != 8) {
			twn.wait(delay_st[i])
			.to({x:textPosx[i],y:textPosy[i],scaleX:1,scaleY:1,alpha:1,rotation:0},1000);
		}

		twn.wait(50).to({x:textPosx[i],y:textPosy[i]},50)
		.wait(100).to({scaleX:2,scaleY:2},50)
		.wait(100).to({scaleX:1,scaleY:1},200)
		.call(finshmove)//shadow
		.wait(max_delay-delay_st[i])//最大値利用時間差吸収
		.wait(10000)
		.to({x:textPosx[i]-canvasWidth},600)//左に全体移動
		.to({x:textPosx[i]+canvasWidth},1)//右に瞬間移動
		.wait(1000)
		.to({x:textPosx[i]},800)//元に戻す
		.wait(500)
		.call(subtext_view)//SUB-TEXT
		.to({scaleX:2,scaleY:2},400)
		.to({scaleX:1,scaleY:1},200)
		.wait(8000)
		.wait(delay_st[i])
		.call(finshmove2)//shadow-clear
		.wait(500)
		.to(params,speed_v)
		.call(function(){
			//終了
			if (textcount == textmax-1) {finshtextAll();}
			textcount +=1;//グローバルの事
		});

		flag *=-1;
	}

}

//SUB-TEXT
function set_subtext () {

	//subtextインスタンスを作る非表示
	//(x,y,w,h,size,fcolor,str)
	subText=createSubtext(0,0,10,10,"20px","#EEEEEE",substring);
	subtextcontainer.addChild(subText);
	subText.visible=false;
}

//テキストshadow
function finshmove() {
	this.shadow=shadow2;
}
function finshmove2() {
	this.shadow=null;
}
//途中でsubtext表示
function subtext_view() {
	if (this.id == 0) {
		//subtextインスタンス表示
		subText.visible=true;
	} else {
		return
	}
}

//テキスト終了
function finshtextAll () {

	//調整
	setTimeout(function() {

		//subtextインスタンス表示
		//subText.visible=true;

		//welcome FadeIn
		welcomePanel.alpha=0;
		welcomePanel.visible=true;
		var tween=createjs.Tween.get(welcomePanel);
		tween.to({alpha:1},1000)
		.call(finshAll);

	},1000);
}

//終了
function finshAll () {

	//全て終了グローバル変数
	allcount +=1;

	//2個のアニメ終了
	if (allcount == 2) {
		//END
		set_text("END");
		//調整
		setTimeout(function() {

		set_text("AUTO");
		//リセット処理
		reset_parts();

		},2000);
	}
}
//reset_parts
function reset_parts() {

	set_text("CLEAR");
	welcomePanel.visible=false;
	subText.visible=false;

	//図形コンテナの中を削除
	circleBase.removeAllChildren();
	//テキストコンテナの中を削除
	maintextcontainer.removeAllChildren();
	stage.update();
	//SUB-TEXTコンテナの中を削除
	subtextcontainer.removeAllChildren();

	//調整
	setTimeout(function() {
		set_prologue();
	},2000);

}

//tickステージ
function tick() {
	stage.update();
}

//背景色変更
function backchg() {
	backchg_no ++;
	if (backchg_no > backcolors.length-1) {backchg_no=0;}
	var color=backcolors[backchg_no];
	backrect.graphics.beginFill(color).drawRect(0,0,canvasWidth,canvasHeight);
}

//Circleインスタンスを作る
function createBall(x,y,r,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	//ベタ
	s.graphics.s().beginFill(c).drawCircle(x,y,r);
	//グラデ、小さいのでさほど効果なし
	//s.graphics.s().beginLinearGradientFill([c,"#FFFFFF"],[0,1],0,0,0,r*2).drawCircle(x,y,r);
	//原点中央
	s.regX=0;
	s.regY=0;
	return s;
}
//Starインスタンスを作る
function createStar(x,y,r,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	s.graphics.s().beginFill(c).drawPolyStar(0,0,r,5,r/2,-90);
	//原点中央
	s.regX=0;
	s.regY=0;
	return s;
}
//Rectインスタンスを作る
function createRect(x,y,w,h,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	s.graphics.s().beginFill(c).drawRect(0,0,w,h);
	//原点中央
	s.regX=w/2;
	s.regY=h/2;
	return s;
}
//RoundRectインスタンスを作る
function createRoundRect(x,y,w,h,r,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	s.graphics.s().beginFill(c);
	operaRoundRect(s,x,y,w,h,r);
	//原点中央
	s.regX=w/2;
	s.regY=h/2;
	return s;
}
//Lineインスタンスを作る
function createLine(x,y,w,h,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	s.graphics.ss(w,"round").s(c).moveTo(0,0).lineTo(0,h);
	//原点中央
	s.regX=w/2;
	s.regY=h/2;
	return s;
}
//Leafインスタンスを作る
function createLeaf(x,y,w,h,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	s.graphics.s().beginFill(c).moveTo(w/2,0).quadraticCurveTo(w,h/2,w/2,h).quadraticCurveTo(0,h/2,w/2,0);
	//原点中央
	s.regX=w/2;
	s.regY=h/2;
	return s;
}
//Ellipseインスタンスを作る
function createEllipse(x,y,w,h,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	var s=new createjs.Shape();
	s.graphics.s().beginFill(c).drawEllipse(0,0,w,h);
	//原点中央
	s.regX=w/2;
	s.regY=h/2;
	return s;
}

//MOVEテキストインスタンスを作る
function createMoveText(size,font,color,rainbow,space,str) {

	var fontdata=size + " " + font;

	var chip=[];
	var len=str.length;
	var tpos_x=0;
	var tpos_y=0;

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

		//文字インスタンスを作る
		var t=new createjs.Text("",fontdata,color);
		//1文字入れる
		t.text=str.charAt(i);
		//幅高さ
		var w=t.getMeasuredWidth();
		var h=t.getMeasuredHeight();
		//原点中央
		t.regX=w/2;
		t.regY=h/2;
		//虹色
		if (rainbow) {
			t.color=rainbowColor(i,len,80,40);//rainbow
			//t.color=createjs.Graphics.getHSL(i/len*360,100,50);//直接
		}
		//位置
		tpos_x +=w/2;//half
		t.x=tpos_x;
		t.y=tpos_y;//0
		//位置加算
		tpos_x +=(w/2+space);//half+space
		//大きさ重要
		t.width=w;
		t.height=h;
		//
		chip[i]=t;
	}
	//戻り配列
	return chip;
}

//CirclePanelインスタンスを作る
function createCirclePanel(x,y,r,color,size,fcolor,str) {
	var c=color;
	var fontdata=size+" Arial";
	var container=new createjs.Container();
	var s=new createjs.Shape();
	//ベタ
	s.graphics.s().beginFill(color).drawCircle(x,y,r);
	//原点中央
	s.regX=0;
	s.regY=0;
	var t=new createjs.Text("",fontdata,fcolor);
	t.text=str;
	//幅高さ
	var w=t.getMeasuredWidth();
	var h=t.getMeasuredHeight();
	//原点中央
	t.regX=w/2;
	t.regY=h/2;
	t.y=-40;
	t.shadow=shadow;
	container.addChild(s,t);
	return container;
}

//subtextインスタンスを作る
function createSubtext(x,y,w,h,size,fcolor,str) {
	//
	var fontdata=size+" Arial";
	var t=new createjs.Text("",fontdata,fcolor);
	t.text=str;
	//幅高さ
	var sw=t.getMeasuredWidth();
	var sh=t.getMeasuredHeight();
	//原点中央
	t.regX=sw/2;
	t.regY=sh/2;
	return t;
}

//VIEWTEXT
function set_text(t) {
	viewtext.text=t;
}

//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);
}

//rainbow-Color2/未使用
var rainbowColor=function(v,cv,saturation,lightness) {
	var no=v/cv*360;
	var color=createjs.Graphics.getHSL(no,saturation,lightness);
	return color;
}

//START
init();

注釈文を削除すれば、幾分早くなります。


CSS

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


/*日本語 createJS028.css*/

#demo-wrap {
position:relative;
width:auto;
height:320px;
text-align:center;
}

#image-box {
/*position:absolute;*/
position:relative;
top:0;left:0;
width:640px;
height:300px;
padding:0;
margin:0 auto;
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:#000000;
}

#image-box #mainCanvas {
border-radius:10px;
}

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

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


簡単な説明


[説明図]


 

● CreateJSのMotionGuidePlugin機能

CreateJSの「MotionGuidePlugin」機能を使用して図形、テキストをパスに沿ってTweenさせるテストをして見る。
複数の「要素」を動かしていて、原点の違いもあり少々面倒な所も有ります。
デモでのガイドのパスは、0地点からでは無く、反対方向からの描画なので注意されたし。(原点0からのパスでも使用は出来ます)

「MotionGuidePlugin」機能は大変便利だ!、インスタンスの状況により修正などが必要だが、設計次第では簡単にも出来るし Twenn で制御出来る事が良い。Tickerで制御するより数段作業が楽になる!


MotionGuidePlugin読み込み

install()を実行しないと使用できないので注意下さい。


//Plugin読み込み
createjs.MotionGuidePlugin.install();

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

これではだめだ??
<script type="text/javascript" src="js/MotionGuidePlugin.js"></script>
この後install()を実行すれば機能するが、2重手間になる

パスに沿ってTweenさせる

基本的に2次ベジェ曲線の軌跡データを与えているだけであるが、対象「要素」が複数であり、パスの形を反転して、Y軸またはX軸で対象形になるようにして再利用。原点の違いで「パス位置を補正」していますので判り難いと思います。


● 円周配置の図形をパスに沿ってTweenさせる

図の「図形インスタンス」の様に「要素」のY方向に「ガイド用パス」を設定してTweenさせるのであるが「要素」のマイナスY方向から0地点に向かって動作する。


Y軸で左右反転させれば、運動パターンが対象形になる、「ガイド用パス」x値を reverse_v でマイナス乗算しているだけである。

「ガイド用パス」を設定してTweenさせても円周配置のため効果が無い時もあるので、遅延時間に工夫、遅延値配列をシャッフルすると効果的だ。(パス形状によりかなり違う)


注意、デモでの「図形インスタンス」の作りと配置の都合で「ガイド用パス」y値はマイナスである。「図形インスタンス」のラップの角度を変えているので「要素」原点は 0 0 である故「要素」毎のパス修正はいらない。
また図形、Line. Leaf. Ellipse、は図では横長だが実際には縦長です。


/ 複数図形使用のため、ガイド用 orient は false にして rotation が機能するようにしています。


var chgflag3=Math.floor(Math.random()*3);
//ガイド反転reverse
var reverse_v=1;
if (chgflag3 == 1) {reverse_v=-1;}

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

var twn2=createjs.Tween.get(ballChip[i])
.wait(delay_st2[i])//Guide
.to({guide:{path:[radius*reverse_v,-radius*2, -radius*reverse_v,-radius,0,0],orient:false},rotation:-45*reverse_v,scaleX:1,scaleY:1,alpha:1},2200)//ガイド反転

始まりのIN にガイドを使用しました。最後のOUTでは通常の処理です。


● 横並びのテキストをパスに沿ってTweenさせる

図の「テキストインスタンス」の様に「要素」のY方向に「ガイド用パス」を設定してTweenさせるのであるが「テキスト要素」の配置の特性上、X方向から0地点に向かって動作させた。(右外側から原点0に向かって)


但し「テキスト要素」が 0地点では無く文字の間隔分離れているので「ガイド用パス」を補正している。


X軸で上下反転させれば、運動パターンが対象形になる、「ガイド用パス」y値をマイナス乗算しているだけである。

「ガイド用パス」を設定してTweenさせても、文字が後になるほど、「移動前文字位置」が欄外にあるのでTween効果が薄い、運動パターンを上下反転に変化させて違いを出した。


/ ガイド用 orient は false にして rotation が機能する様にしています。


【重要】テキストが表示位置に達した直後に rotation:0 を実行しています。旨く角度等が繋がらない時が有りますので、次のアクションでゴマカシ(瞬間的に、回転、拡大縮小、1回転半捻り、位置をずらす、等の操作)を入れて、さも連続している様に装います。ここが重要な「ゴマカシ」ポイントになります。旨く行かなければ当然「知らんふり」です。


図の「テキストインスタンス」


var chgflag3=Math.floor(Math.random()*3);
//ガイドreverse反転/mutual交互
var mutual=false;
var reverse_v=1;
if (chgflag3 == 1) {reverse_v=-1;}//反転
if (chgflag3 == 2) {mutual=true;reverse_v=1;}//交互

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

上下交互にする flagは交互に 1 -1 の値
if (mutual) {reverse_v *=flag;}//ガイド波形交互

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

最初はif文で書いています

IN
if (pattern_no == 8) {
	twn.wait(delay_st[i])
	.to({guide:{path:[movePosx[i],movePosy[i], guide_w*.75*chg_v+textPosx[i],guide_w/2*.75*reverse_v,guide_w/2*chg_v+textPosx[i],0, guide_w*.25*chg_v+textPosx[i],-guide_w/2*.75*reverse_v,textPosx[i],textPosy[i]],orient:false},rotation:0,scaleX:1,scaleY:1,alpha:1},2000)//左右用
	.to({x:textPosx[i],y:textPosy[i],rotation:0},50);
}

[movePosx[i]は事前、に+値、又は -値に処理している
---------------------------------------------------

最後は変数paramsを代入して処理

OUT、start end で方向をかえています
var params;
if (pattern_no != 8) {params={x:movePosx[i],y:movePosy[i],scaleX:keepScale,scaleY:keepScale,alpha:0,rotation:keepRote};}
if (pattern_no == 8) {params={guide:{path:[movePosx[i],movePosy[i], guide_w*.75*chg_v+textPosx[i],guide_w/2*.75*reverse_v,guide_w/2*chg_v+textPosx[i],0, guide_w*.25*chg_v+textPosx[i],-guide_w/2*.75*reverse_v,textPosx[i],textPosy[i]],orient:false,start:1,end:0},scaleX:keepScale,scaleY:keepScale,alpha:alpha_no,rotation:keepRote};}//左右用

実行
.to(params,speed_v)

デモでは、パターン番号 8 の時にガイド処理しています。


1. 最後の OUT拡散 でもガイドを使用しました。(ガイドの start end を使えば簡単に方向を変えられる)
2. 最後のTween速度を 1500 にしているが、ガイドを使用していない場合は 800-1000 でも良い。デモでは、変数で速度を変えている。(ガイドを使用時はTween時間が長いほうが動きがわかる)
3. 遅延時間の保存配列 delay_st を reverse() で逆転(delay逆転)していますが、計算方法の違いで無意味の時が有ります。
4. ガイド使用のテキストの動きは、拡大率、透明度、遅延時間、初期位置、などの条件で見た目が違いますので了承下さい。(横長の感じになり易い、時には効果なしも有り得る)
5. Tweenを繋いで合わなくなったら、当然「ゴマカシアクション」でシノギます。つまり「イイカゲン」に作る事が大事だ!。(何でも動けば人は歓喜しノタウチ回り、次の瞬間「ウットウシイ」と感じ飽きる)
6. 文字色はレインボー色(虹色)になっていますが指定色も使用可能です。


テキストOUT処理

start:1,end:0 が加わっただけで IN の処理と全く同じである。必要ならscale alphaなど変えて動作に変化を付ける。
ただ、直感的にパス情報が IN と同じなので面食らう。


その外

MAIN-TEXTの「テキスト要素」にshadow処理をしていますが、最後のOUTで重く成るので途中削除している。マシン環境がよければshadow処理のままでもOKだ。
ガイド用パス及びパスコンテナは役目を終えているので、削除しても良い。(暑中見舞いに置いときます)


FLASH(as)でガイドを引きインスタンスを実際に動かし、タイムラインに色々書き込み「手作業で作っている感覚」そのものである。昔の作業をテキストエデッタで再現しているに過ぎない。




● ガイド用パスのX軸またはY軸の反転

デモでは、下図の様にサイン波の様な曲線を2つの曲線を連続に描いてガイドに使用しています。
パスを描いた場合に、X軸またはY軸で反転してみると、単純に描画データを反転すれば良いことに気がつきます。
1つで4倍「オイシイ」!、、、消費税も4倍かも知れない。

[説明図]


テキスト用パスの場合を例に考えます。パスは右側より原点 0 0 に向かって描かれています。(上図参照)

1. X軸に対して反転する時はパスの y値 をマイナス乗算、
2. Y軸に対して反転する時はパスの x値 をマイナス乗算、

すれば、1つのパスデータで4パターンに変化できます。(start end を使用すればスタート位置を変えられる)


「左右よりパスに沿ってTween」させる事も可能に成ります。面倒なだけですが!、デモでは

chg_v は 1 または -1 の値を持ちます。 Y軸反転
reverse_v は 1 または -1 の値を持ちます。X軸反転
movePosx[i]は「要素」のX位置ですが、事前に左右に移動している値ですからそのまま利用します。
guide_wはパスの描く全体の幅です。幅を変えれば同じ曲線のスタイルで拡大または縮小します。(通常左右に移動する距離が全体幅に成る、合わない場合は変な動作になる)
このテキスト用では文字の位置が違いますのでその距離分修正しています。+textPosx[i]
pattern_no=8 はパスに沿ってTween」させる識別のパターン番号です。


つまり、下の Tween は外側から原点に向かう「4つのガイド波形」に使用することが出来ます。start end は省略していますから実際は、start:0 end:1 が設定されたと同じです。

よって、start:1 end:0 を設定すれば、原点0から外側に運動することになります。これは OUT に使用しました。



IN
.to({guide:{path:[movePosx[i],movePosy[i], guide_w*.75*chg_v+textPosx[i],guide_w/2*.75*reverse_v,guide_w/2*chg_v+textPosx[i],0, guide_w*.25*chg_v+textPosx[i],-guide_w/2*.75*reverse_v,textPosx[i],textPosy[i]],orient:false},rotation:0,scaleX:1,scaleY:1,alpha:1},2000)//左右用

OUT
var params;
if (pattern_no == 8) {params={guide:{path:[movePosx[i],movePosy[i], guide_w*.75*chg_v+textPosx[i],guide_w/2*.75*reverse_v,guide_w/2*chg_v+textPosx[i],0, guide_w*.25*chg_v+textPosx[i],-guide_w/2*.75*reverse_v,textPosx[i],textPosy[i]],orient:false,start:1,end:0},scaleX:keepScale,scaleY:keepScale,alpha:alpha_no,rotation:keepRote};}//左右用
.to(params,speed_v)

OUTは、params変数代入ですが、guide: 部分は start:1 end:0 が加わっただけで同じだ。


実際のパス


function draw_line() {
	var reverse_v=1;
	var guide_w=canvasWidth;
	var path = new createjs.Shape();
	path.graphics.beginStroke("#ff0000").moveTo(guide_w,0);
	path.graphics.curveTo(guide_w*.75,guide_w/2*.75*reverse_v,guide_w/2,0);
	path.graphics.curveTo(guide_w*.25,-guide_w/2*.75*reverse_v,0,0);
	pathcontainer.addChild(path);
}

実際にパスを描き、設計通り動作するかの確認が必要です。
draw_line()、draw_line2()にグラフィックがあるので、パスコンテナ(pathcontainer)に描いて位置を合わせ確認した。


● ガイド用パスの実行範囲の指定

詳しい事はいえませんが、start end をテストして見たその結果です。

1. ガイドのデータである、パスの始点(moveToの記述のあるポイント)が、start:0 終点が、end:1 である。(記述を省略しても変わらない、 初期値が start 0、end 1 である)
2. start:1 end:0 を指定すれば、逆向きにTweenが実行される。(ガイドのパスデータはそのまま)
3. 登録のガイドのデータ範囲内、start end でTween実行の範囲を指定出来る。範囲外はスキップされるので便利だ。但し、Tween実行時間は変わらないのでゆっくりの動作になる。
4. 図の様に start:.5 end:1 と指定した場合は、登録パスの半分が実行される。当然移動範囲は半分になるしアニメ時間は変わらない。(0-1の範囲指定だから多少誤差は出る)
5. start:0 end:.75 と指定した場合は、範囲内がTweenして範囲外の部分がスキップされ、最後は終点位置に移動する。計画的に使用すれば便利だ。


以上は、現時点でのテストの結果である。詳細の説明などはないので使用に当たっては注意されたし。正確には「詳しくは書いてない英文、CreateJSマニュアル」参照下さい。
間違いなどありましたら、勝手に修正、解釈下さい。


● CreateJSのMotionGuidePlugin機能のCreateJSマニュアルからの説明

「ガイド用パス」は curveTo() で描かれる曲線(2次ベジェ曲線)で複数を連続でき、moveTo() は最初の位置情報になる様です。通常の使用にはcurveTo()で間に合うと思います。

但し、その外の「パス」の場合は面倒の様だが、これの実際の例が jsdo.it に掲載されている。(エライ大変な処理が必要みたい、ワタシはやめときます)


【参考】jsdo.it記事: CreateJSでパスに沿ってモーショントゥイーンで回転


CreateJSマニュアル、http://www.createjs.com/Docs/TweenJS/classes/MotionGuidePlugin.html


Example
//Using a Motion Guide
	createjs.Tween.get(target).to({guide:{ path:[0,0, 0,200,200,200, 200,0,0,0] }},7000);
//Visualizing the line
	graphics.moveTo(0,0).curveTo(0,200,200,200).curveTo(200,0,0,0);

ガイドオブジェクトのプロパティ

1. ガイド用パスは、curveTo() で描かれる曲線のデータを直接当てはめれば良い。
2. start: 0-1、記載無ければ 0、仮に、start 1、end 0、でパスの反対方向から実行する。
3. end: 1-0、記載無ければ 1、また start、end、の値で途中からの「パス」も実行出来る。
4. orient: true false、trueでガイド用パスに平行な角度にする。(特定の位置に合わせる)
5.「パス」は使い回しせず、その都度記載した方が良いみたいだ。問題が起きる場合があると説明されている。


以上、CreateJSマニュアル抜粋。


このページのデモは、以下の記事のデモを組み合わせて MotionGuideTween を追加したものであるが、一部は効率化している。

【参照】当方の記事: CreateJS テキストアニメーション ランダム設定のテスト レインボー色(虹色)

【参照】当方の記事: CreateJS 図形を円周に配置した、サークルアニメーション、Tweenをwait()で連続操作する(虹色)


 

その他

DEMO専用です。使用する事は可能ですが、あくまでもテストですので個人の判断で使用下さい。画像などは各自ご用意下さい。JSの先頭部分をお読み下さい。
但し、効率的な方法があれば、予告無くスクリプトなど修正する場合があります、ご了承下さい。


アイスが美味しい時節に相成り、謹んで暑中見舞い申し上げます。夏に涼しい「複雑怪奇」説明ですが、以上です。

 


[ この記事のURL ]


 

ブログ記事一覧

年別アーカイブ一覧



[1]