POPSブログ

CreateJS 3D Carousel もどき、Matrix2Dで円周上に配置したインスタンスを回転させる

271

  Category:  javascript2014/01/14 pops 

円周上に配置したインスタンスを回転させる 3D Carousel もどきですが「2D」で作成しています。Matrix2Dクラスを使用していますので、問題となる重なり順をsort()を使用して修正しています。
中に画像を配置していない、簡略化した原型です。
easeljs-0.7 でのテストです。

 

CreateJS Carousel Matrixで円周上に配置したインスタンスなどを回転


これは、3D Carousel もどきです。Matrix2Dクラスですから3Dには成りませんので、それに近いようにするために画像の重なり順を修正しています。参考にした AS3 3D Carouselと同じです。
実際の3Dには及びませんがマアマア3Dに近い効果は出ています。Matrix処理のため結構軽く回転するようです。


easeljs-0.8のMatrix変更

最新のeaseljs-0.8ではMatrixクラスが変更になり、以前のスクリプトは正常に動作しませんのでeaseljs-0.8用に修正しています。詳細は下記記事を参照ください。

【参照】当方の記事: CreateJS 3D風Carousel円周配置のインスタンスを回転させる(easeljs-0.8)


 

DEMO


CreateJS Carousel Matrix2Dで円周上に配置した画像などを回転 テスト、(createJS056.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>

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


HTML (HTML5)


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

JS

createJS056.js、JS名は任意に変更可。注意、easeljs-0.7用です。


//日本語
//createJS056.js
//Carouselデモ1
//easeljs-0.7用

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

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

//画像の大きさ
var imageWidth=100;
var imageHeight=100;

//carouselの色
var carouselbackcolor="#FFFFFF";

//split分割数自動修正 3-12
var carouselsplit=12;

//半径
var radiusX=240;//X方向
var radiusY=80;//Y方向

//回転速度 デグリー値1-5
var angle=2;//DEG
//正転逆転フラグ 右正転=1 左逆転=-1
var carouselflag=1;
//マウスイベントの使用 true false
var eventUse=true;
//コンテナ角度
var containerAngle=0;
//パーツ角度
var partsAngle=0;
//虹色にする不透明 true false
var rainbowColor=true;

//基本スケール
var stScale=0.75;
//始点の角度
var startAngle=-Math.PI/2;

//テキスト使用
var textUse=true;

//--------------------------------------------------------------
//回転正逆補正
angle *=carouselflag;
//to-RAD
var rotate_angle=angle*createjs.Matrix2D.DEG_TO_RAD;
var plusAngle=-Math.PI/2;
//--------------------------------------------------------------

var stage;
var backimage;
var carouselContainer;
var progresscontainer;
var loadingShape;
var viewtext;

//matrix
var matrix=new createjs.Matrix2D();
var chip=[];
var innerchip=[];
var stopNo=1;

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

function init() {

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

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

	//空コンテナインスタンス配置
	carouselContainer=new createjs.Container();
	carouselContainer.x=canvasWidth/2;
	carouselContainer.y=canvasHeight/2;
	stage.addChild(carouselContainer);

	//簡易TEXT
	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);

	//TEXT
	set_text("Carousel");

	//画像表示に進む
	carousel_set();

}

//
function carousel_set() {

	//パーツを配置してBaseを作る/Bitmap画像
	set_baseCircle();

	//コンテナを傾ける
	carouselContainer.rotation=containerAngle;

	//Listener
	createjs.Ticker.setFPS(40);
	createjs.Ticker.addEventListener("tick",carousel_rotate);

	//マウスイベントの使用
	if (eventUse) {
		stage.addEventListener("stagemousemove",setAngle);
	}

}

//マウスアクション
function setAngle(event) {
	var mouseX=event.stageX;
	angle=(mouseX-canvasWidth/2)*1/50;
}

//Ticker
function carousel_rotate(event) {

	var count=carouselsplit;

	//加算
	plusAngle +=(angle*createjs.Matrix2D.DEG_TO_RAD);

	//描画データ
	var cx,cy;
	var cAngle=startAngle;
	var nAngle=0;

	//描画位置
	nAngle=Math.PI*2/count;
	var chipflag=1;
	var parts_angle=0;

	//保存POSITION配列
	var posArr=[];
	//SORT用POSITION配列
	var zArr=[];
	//透明度用配列
	var alphaArr=[];

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

		//回転
		cAngle +=nAngle;

		cx=Math.cos(cAngle+plusAngle)*radiusX;
		cy=Math.sin(cAngle+plusAngle)*radiusY;

		//alpha-Data計算
		alphaArr[i]=(1+cy/radiusY)/2;

		//配置角度取得
		parts_angle=Math.atan2(cy,cx);

		//パーツ角度
		var angle_r=partsAngle*createjs.Matrix2D.DEG_TO_RAD;

		//伸縮1 Xスケールで向きを変える
		var scl=Math.cos(parts_angle+Math.PI/2)/Math.cos(Math.PI);//Xスケール
		//var scl=Math.cos(parts_angle+Math.PI/2);//Xスケール反対向き
		//伸縮2 遠近感を付ける
		var sclxy=(Math.abs(Math.cos(parts_angle/2-Math.PI/4)/Math.cos(Math.PI))+1)/2;
		//position段階計算
		var a_v=1+(cy*0.25)/radiusY;
		posArr[i]=a_v;

		//sort-DATAを保存
		var id=chip[i].id;
		//オブジェクトで保存
		zArr.push({'id':id, 'position':cy});

		//matrix
		matrix.identity();
		//順序重要
		matrix.rotate(angle_r);

		//伸縮1実行
		//matrix.scale(scl*stScale,stScale);
		//伸縮2実行
		//matrix.scale(sclxy*stScale,sclxy*stScale);
		//position段階スケール実行
		//matrix.scale(posArr[i]*stScale,posArr[i]*stScale);
		//基本スケール実行
		matrix.scale(stScale,stScale);

		//位置をずらす
		matrix.translate(cx,cy);
		matrix.decompose(chip[i]);

	}

	//配列をソート/positionの昇順にsort
	zArr.sort(function(a,b) {
		var x=a.position;
		var y=b.position;
		return x > y ? 1 : -1;}
	);
	//INDEX alpha修正
	for (var i=0; i < zArr.length; i++){
		var idno=zArr[i].id;
		//Index
		carouselContainer.setChildIndex(chip[idno],i);
		//alpha
		//chip[idno].alpha=alphaArr[idno]+0.2;
	}

	//その他の処理 update
	draw_carousel();


}

//base
function set_baseCircle() {

	//パーツの大きさ
	var parts_w=imageWidth;
	var parts_h=imageHeight;
	//数
	var count=carouselsplit;

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

		//図形用インスタンス配置
		chip[i]=new createjs.Container();
		chip[i].id=i;

		//Rect
		innerchip[i]=new createjs.Shape();
		//chip[i].shadow=shadow;//shadow重い

		//Rect2
		graphicRect2(innerchip[i],-parts_w/2,-parts_h/2,parts_w,parts_h,carouselbackcolor,rainbowColor,count,i);

		//addChild
		chip[i].addChild(innerchip[i]);
		carouselContainer.addChild(chip[i]);

	}

}


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

//Rect Graphics
function graphicRect2(s,x,y,w,h,color,rainbow,len,no) {
	var c=color;
	if (rainbow) {
		c=createjs.Graphics.getHSL(no/len*360,100,50);//直接
	}
	//s.graphics.clear();
	s.graphics.ss(1).s("#555555");//LINE
	s.graphics.beginFill(c).drawRect(x,y,w,h);//FILL
}

//VIEWTEXT
function set_text(t) {
	if (textUse) {
		viewtext.text=t;
		stage.update();
	}
}

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

init();

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


CSS

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


/*日本語 createJS056.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;
}

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


簡単な説明


[説明図]

 

Container()インスタンスを作り外周に配置して並べている簡単な作りで、中にShape()インスタンス、画像等を収容して、Matrixで外周位置を変化させて回転する仕組みです。
CreateJSのMatrix(Matrix2Dクラス)は3Dに対応していませんので、重なり順をtick毎に修正しています。動きが3Dに見えますが実際には2Dで出来ています。


1. 通常の Matrix (Matrix2D)で処理しています。
2. 円周配置位置は、matrix変換、transformPoint()を利用しても可能ですが、クセがあるのでここでは使用しません。通常の Matrixで処理したほうが理解し易いでしょう。
3. マウス位置に対応して、回転方向速度が変わります。(マウスイベント解除も可能)
4. インスタンス重なりの修正(深度)はsortで行なっています。
5. 実際には色々と画像などを挿入加工して使用する事になると思います。
6. 一応 easeljs-0.7.1でも可動します。


一番の問題は画像の重なりを旨く制御して3Dに見せる事ですが、「clockmaker」さんの「3D Carousel Flash 10 で被写界深度ネタ Z-sortつき」の処理方法を参考にさせて戴きました。

【参考】wonderfl.net記事: 3D Carousel (Flash AS3)


重なり順の修正

3Dでは有りませんので、円周配置のインスタンスの重なりが変になります。これを修正しなければなりませんが、常にインスタンスの位置が変わるので大変面倒です。その為に「上記記事」を調べて同様の方法で解決しました。


1. この、Carouselでは奥から手前の順に重なりが、常に一定である。つまり「配置Y方向値」の順序である。
2. ここでは、2D処理であるから「被写界深度」はインスタンスの重なり順序、z=重なり順、と置き換えて考えて、インスタンスのIDに記載しておいて常に参照すれば良い。
3. tick毎に「配置Y方向値」を並び替えて、該当するz値(インスタンスのID)で補正すれば良い。
間違いなく動作するので処理は正しいものと思う。
4. AS3、sortOn()、はないから、通常の「連想配列」のsort()を使用した。
5.「配置Y方向値」を直接並び替えたが、0-1の値に変換してから処理しても良いだろう。
6. javascript 「連想配列」のsort()で、データはオブジェクトで配列に入れて操作した。
7. ここでのsort()の結果は 昇順 に成ります。
8. 幾つか、他の方法でも試してみた、ある程度は修正するが、完全には処理出来なかった。


重なり順、透明度設定を一緒に処理しているので、下記に抜粋した。


事前にIDを登録
//base
function set_baseCircle() {

	略す

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

		//図形用インスタンス配置
		chip[i]=new createjs.Container();
		chip[i].id=i;

		略す
	}
}

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

Ticker毎の処理
//Ticker
function carousel_rotate(event) {

	略す

	//保存POSITION配列
	var posArr=[];
	//SORT用POSITION配列
	var zArr=[];
	//透明度用配列
	var alphaArr=[];

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

		略す

		cx=Math.cos(cAngle+plusAngle)*radiusX;
		cy=Math.sin(cAngle+plusAngle)*radiusY;

		//alpha-Data計算
		alphaArr[i]=(1+cy/radiusY)/2;

		略す

		//sort-DATAを保存
		var id=chip[i].id;
		//オブジェクトで保存
		zArr.push({'id':id, 'position':cy});

		略す

	}

	//配列をソート/positionの昇順にsort
	zArr.sort(function(a,b) {
		var x=a.position;
		var y=b.position;
		return x > y ? 1 : -1;}
	);
	//INDEX alpha修正
	for (var i=0; i < zArr.length; i++){
		var idno=zArr[i].id;
		//Index
		carouselContainer.setChildIndex(chip[idno],i);
		//alpha
		//chip[idno].alpha=alphaArr[idno]+0.2;
	}

	略す

}

配列をソートの部分は、オブジェクト配列をソートしているので書き方に注意下さい。


参考スクリプト、「clockmaker」さん、(AS3) 3D Carousel Flash 10 で被写界深度ネタ Z-sortつき


// Flash 10 で被写界深度ネタ Z-sortつき
package{
  import flash.display.*
  import flash.events.*
  import flash.filters.BlurFilter;
  import flash.geom.*;
  
  [SWF(frameRate="90", width="465", height="465")]
  public class Main extends Sprite {

  public function Main() {
      
      var main :Sprite = Sprite(addChild(new Sprite()))
      main.x = stage.stageWidth / 2
      main.y = stage.stageHeight / 2
      var wrap :Sprite = Sprite(main.addChild(new Sprite()))
      
      var pp:PerspectiveProjection = root.transform.perspectiveProjection; 
      
      var objs:Array = []
      for(var i:int=0; i<15; i++)
      {
        var sp: Sprite = Sprite(wrap.addChild(new Sprite()))
        sp.graphics.beginFill(0xFFFFFF * Math.random())
        sp.graphics.drawRect(-25, -25, 50, 50)
        sp.x = 200 * Math.sin( i * 360 / 15 * Math.PI / 180)
        sp.z = 200 * Math.cos( i * 360 / 15 * Math.PI / 180)
        
        objs.push(sp)
      }
      
      stage.addEventListener(Event.ENTER_FRAME, function(e:Event):void
      {
        pp.projectionCenter = new Point(stage.stageWidth / 2,
                  stage.stageHeight / 2 - 100)
        
        wrap.rotationY += (mouseX / stage.stageWidth * 480 - wrap.rotationY) * 0.05
        
        var arr:Array = []
        for (var i:int=0; i<objs.length; i++) {
          var ele:Sprite = objs[i] as Sprite
          ele.rotationY = -wrap.rotationY
          var mtx:Matrix3D = ele.transform.getRelativeMatrix3D(main)
          arr.push( { ele:ele, z:mtx.position.z } )
        }

        arr.sortOn("z", Array.NUMERIC | Array.DESCENDING)
        var baseZ:Number = wrap.z
        for (i=0; i<arr.length; i++) {
          ele = arr[i].ele as Sprite
          var z:Number = arr[i].z
          wrap.setChildIndex(ele, i)
          var b:Number = Math.abs(z)
          
          // focus depth blur effect, warning, very slow:
          b = z/10
          ele.filters = (b > 2) ? [new BlurFilter(b, b, 3)] : []
        }
      })
    }
  }
}

随分前のスクリプトですが、画像をいれて実際に作り動作させているが、詳細までは検討していませんでした。
今回はこの、スクリプトで助かった。アリガトサン


インスタンスのスケール変更など


個別にインスタンスのスケール変更などにより「方向」「透明度」を変えたりが可能です。


初期では、「基本スケール実行」になっています。JS先頭での切り替えはしませんので、必要な設定をして下さい。
計算方法、Matrix2D、の変更などは自由です。伸縮1の2番目は画像が反対向き(裏返し)になります。
呼称はダイタイの意味です。それぞれの相違は「デモ」で確認下さい。


スケール関連


基本スケール

//基本スケール
var stScale=1.0;

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

計算

//伸縮1 Xスケールで向きを変える
var scl=Math.cos(parts_angle+Math.PI/2)/Math.cos(Math.PI);//Xスケール
//var scl=Math.cos(parts_angle+Math.PI/2);//Xスケール反対向き

//伸縮2 遠近感を付ける
var sclxy=(Math.abs(Math.cos(parts_angle/2-Math.PI/4)/Math.cos(Math.PI))+1)/2;

//position段階計算
var a_v=1+(cy*0.25)/radiusY;
posArr[i]=a_v;


//伸縮1実行
//matrix.scale(scl*stScale,stScale);

//伸縮2実行
//matrix.scale(sclxy*stScale,sclxy*stScale);

//position段階スケール実行
//matrix.scale(posArr[i]*stScale,posArr[i]*stScale);

//基本スケール実行
matrix.scale(stScale,stScale);

透明度関連

最後の数値を変えると、上方(遠方)の透明度を合わせることが可能です。初期状態では設定していません。


	//INDEX alpha修正
	for (var i=0; i < zArr.length; i++){
		var idno=zArr[i].id;
		//Index
		carouselContainer.setChildIndex(chip[idno],i);
		//alpha
		//chip[idno].alpha=alphaArr[idno]+0.2;
	}

ブラー関連

ぼかし処理も可能と思いますが?、実際に処理はしていません。キャッシュしなければならないので抵抗がある。
そのため「上記」の様に透明度で変化させた。


景観方向を変える

現在は、上方より俯瞰した状態で表示しています。下方から見上げる景観にするには、次ぎの変更で可能です。
1. 半径Y方向値をマイナス設定にする。
2. 配列のソートを降順にする。



//半径
var radiusX=240;//X方向
var radiusY=-80;//Y方向

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

//配列をソート/positionの降順にsort
zArr.sort(function(a,b) {
	var x=a.position;
	var y=b.position;
	return x < y ? 1 : -1;}
);

回転速度、正転逆転

回転速度、正転逆転等は初期設定で設定可能ですが、マウスイベントの使用ではマウス位置により、強制的に変更なります。
マウスイベントでの回転速度の変更は直接、関数中の値を変更下さい。


//回転速度 デグリー値1-5
var angle=2;//DEG
//正転逆転フラグ 右正転=1 左逆転=-1
var carouselflag=1;
//マウスイベントの使用 true false
var eventUse=true;

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

//マウスアクション
function setAngle(event) {
	var mouseX=event.stageX;
	angle=(mouseX-canvasWidth/2)*1/50;
}

画像などの挿入

画像挿入のCarouselは、次回、次ページに掲載します。


これらの改造などは自由です。


easeljs-0.8のMatrix変更

最新のeaseljs-0.8ではMatrixクラスが変更になり、以前のスクリプトは正常に動作しませんのでeaseljs-0.8用に修正しています。詳細は下記記事を参照ください。

【参照】当方の記事: CreateJS 3D風Carousel円周配置のインスタンスを回転させる(easeljs-0.8)

【参照】当方の記事: CreateJS 3D風画像Carousel表示(easeljs-0.8)

【参照】当方の記事: CreateJS 3D風画像CarouselのBlur処理(easeljs-0.8)

【参照】当方の記事: CreateJS 3D風Carousel画像拡大表示(easeljs-0.8)

【参照】当方の記事: CreateJS 3D風TEXT文字Carousel表示(easeljs-0.8)



 

一応、完動しますが全てテストです。効率化のため、予告無くJSなど修正する場合がありますので了承下さい。

CreateJSは結構「仕様」が変わりますので特に注意が必要です。


画像挿入形は次回に掲載します。以上です。

 


[ この記事のURL ]


 

ブログ記事一覧

年別アーカイブ一覧



[1]