[MooTools Tutorial] MooTools用プラグインの作り方


Step4: 画像の拡大

Step3で作成したopenメソッド冒頭でローカル変数を2つ作成。

  • var self = クラスオブジェクト(this)を格納
  • var num = initializeで保存しておいたキー番号をretrieveで呼び出して格納(Step3にも登場)
open:function(el,image){
	var self = this;
	var num = el.retrieve("index");
	this.morph[num].start({'opacity':0.3});
}

Step3の透明度モーフィングの後に処理をするため、chainメソッドを使う。

open:function(el,image){
	var self = this;
	var num = el.retrieve("index");
	this.morph[num].start({'opacity':0.3}) //←セミコロンは削除
	.chain(function(){
	})
}

超はしょって説明すると、chainはコールバック関数を付け加えるもの…かな。クラスメソッドに有効で拡張メソッドには使えない。
ある関数の処理が完了してから次の関数を開始する、といった具合に関数を順番に実行させたいときに使う。
例として、
Object.chain(function1).chain(function2).chain(function3);
とした場合はfunction1から順に実行される。実行がつなげた順になるというだけで、結果が開始と同じ順になるとは限らない。

画像の読み込みはAssetsを使う。
これは前のチュートリアルで何度か取り上げているが、XHRでファイルを取得するMoreのクラス。
必須引数である読み込み対象のファイル名はサムネイル画像のsrcを置換する。

open:function(el,image){
	var self = this;
	var num = el.retrieve("index");
	this.morph[num].start({'opacity':0.3}) //←セミコロンは削除
	.chain(function(){
		//フル画像のプリロード Class Assets
		new Asset.image(image.src.replace("_s.","_b.")});
	})
}

読み込みが完了した時に実行されるonloadメソッドの引数には読み込んだ画像のオブジェクトが格納される。
それから得た画像のサイズをstoreで保存してから、Fx.Morphにより拡大する。

.chain(function(){
	//フル画像のプリロード Class Assets
	new Asset.image(image.src.replace("/s_","/b_"),{
		// @param {Object} e HTML IMG Object
		onload: function(e){
			
			//拡大サイズの保存
			image.store("width", e.width );
			image.store("height", e.height);
			
			//サムネイルモーフィング(open)
			self.morph[num].start({
				width: e.width,
				height: e.height
			});
		}
	});
})

このままだと属性とかがサムネイルのままなので、さらにchainを繋げて画像の属性を変更する。
ついでにカーソルをデフォルトに戻しておく。

.chain(function(){
	image.width = image.retrieve("width");//横幅変更
	image.height = image.retrieve("height");//高さ変更
	image.src = image.src.replace("_s.","_b.");//src変更
	self.morph[num].start({'opacity':1});//透明度down
	el.setStyle("cursor","default");//cursor:default
});

これで、クリック→ちょっと透けてローディング見える→拡大 というロジックが完成。

ここまでのサンプル

Step5: コメントを隠す

出っぱなしになっているコメントをスライドで隠そう。

先ずオプションにスライド用のDrationとTransitionを増やす。
冗長して見難くはなるが、任意で変えられそうな値はオプションにしといた方が後々楽。

options: {
	infoClass:".info",//スライドさせる要素のクラス
	thumbClass:".thumb",//サムネイル画像の親クラス
	imgWith:100,//サムネイルの初期横幅
	thumbDration:700,//サムネイルの遅延
	infoDration:800,//情報の遅延
	tumbTransition:Fx.Transitions.Back.easeOut,//サムネイルのトランジション
	infoTransition:Fx.Transitions.Expo.easeOut//情報のトランジション
},

Fx.Slideは任意の要素を縦または横方向に引き出しの如くなめらかにアニメーションさせるMoreのクラス。
このプラグインではコメントの表示・非表示に利用してます。

コメントのスライドはmorphと同じくインスタンスを保存するので、入れ物を用意しておく。

this.morph = [];
this.slide = [];

$eachの中でFx.Slideのインスタンスを作る。Fxなので書式はMorphとほぼ同じ。
第一引数にオプションで設定されたクラス名を持つ要素を指定するため、getElement()メソッドを使う。

self.slide[index] = new Fx.Slide(el.getElement(self.options.infoClass),{
	duration: self.options.infoDration,
	transition: self.options.infoTransition,
}).hide();

hide()メソッドを直接くっつけるだけで初期状態が非表示になる。
これだけでコメントが全て隠れたはず。

Step5.2: 画像が拡大した時に連動させる

Assetのonloadに一行slideInメソッドを付け加えるだけでOK。
拡大した後でスライドさせるなら次のchainに追加する。

new Asset.image(image.src.replace("_s.","_b."),{
	
	onload: function(e){
		
		//拡大サイズの保存
		image.store("width", e.width );
		image.store("height", e.height);
		
		//サムネイルモーフィング(open)
		self.morph[num].start({
			width: e.width,
			height: e.height
		});

		self.slide[num].slideIn();//スライドイン
	}
});

Step6: サムネイルの縦横サイズを保存

開く方が出来たので閉じるロジックを作成していくが、画像を元通り縮小させるにはサムネイルのサイズが必要になる。
全ての画像が同じサイズなら、オプションで新たに縦横サイズを追加すればいいが、
それぞれ異る画像サイズのサムネイルを使いたいので、initializeする時にメンバ変数へ保存しておくことにする。
※ここで配列にするかstore()にするかは作者の好み。

サイズを格納する入れ物を用意する。

this.size = [];//width and height

サムネイル画像自体は$eachで取得しているので、サイズはgetSize()で取得出来る。
用意した入れ物にgetSize()の返り値(widthとheightのオブジェクト)を入れていく。

$each(this.lists,function(el,index){	
	var image = el.getElement(self.options.thumbClass).getFirst("img");//サムネイル画像取得

	self.size[index] = image.getSize();//元サイズを格納
	
	//---省略----

getSizeメソッド使わない場合getSize()は次のようになる。

self.size[index] = { x: image.width, y:image.height };

クリックでオープンメソッドを実行する時に保存された値と横幅を比較するように修正しておく。

if(image.width == self.size[index].x){
	self.open(el,image);
}

これで、$eachが終った時点でthis.sizeには[{x:100,y:68},{x:100,y:122}….]と各サムネイルの初期サイズが格納される。

ここまでのサンプル

Step7:クローズロジック

前に拡大した画像をクリックした時に元に戻す。
新しくクリックされたものかそうでないものかの判別をして、既に拡大されているものだけを縮小するロジックを作る。

その要素がアクティブかどうか判別する方法はいくつかある。

  1. プロパティ値が初期と同じか比較
  2. Class名の有無を比較
  3. フラグを立てて比較

(2)は個人的にはあんまり使わない。なんとなくClass内部でこっそりやってる感じがイイと思うので。
(1)だとこの場合サイズの比較になるが、そうなると配列に保存された元サイズとの比較になり、
配列からサイズを引き出すには拡大されている画像のインデックス番号が必要なので、
それならインデックス番号をフラグにすればよい。というわけで今回は(3)の方法を取る。

initializeにアクティブになった時にインデックス番号を保存する入れ物を作る。

this.active = null;//アクティブな要素

openメソッド内でcloseメソッドを呼び出す。
実行条件はthis.activeに値があった場合のみ。

//アクティブな要素を閉じる
if(this.active != null) this.close();

openメソッドの一番最後でアクティブになった要素のインデックス番号を保存する。

this.active = num;//アクティブな要素のインデックスを保存

こうすると、ページ開いたばかりの初期状態はthis.activeがnullなのでclose()が実行されない。

閉じるのは開くのと逆なのでcloseメソッドのソースはopenと似たような感じになる。

close:function(el){
	var self = this;
	
	this.morph[this.active].start({'opacity':0.3})
	.chain(function(){
		this.start({
			width:self.size[self.active].x,
			height:self.size[self.active].y
		});
		self.slide[self.active].slideOut();//スライドアウト
	})
	.chain(function(){
		this.element.width = self.size[self.active].x;
		this.element.height = self.size[self.active].y;
		this.element.src = this.element.src.replace("_b.","_s.");
		self.morph[self.active].start({'opacity':1});
		self.lists[self.active].setStyle("cursor","pointer");
	});
}

ここまでのサンプル

ここまでのソースを実行すると、閉じるアクションは出来ているものの
縮小後のサイズが違ったりフェードが機能していなかったりする筈。
これはthis.activeの値がclose()のchainを実行する前に
open()最後尾のthis.active=numによりクリックした要素の番号に変わってしまっている為だ。
これを防ぐ為にthis.activeの値をclose()に引数として渡す。

呼び出し側

if(this.active != null){
	this.close(el, this.active);
}

関数側

/* 関数側 */
close:function(el, act){
	var self = this;
	
	this.morph[act].start({'opacity':0.3})
	.chain(function(){
		this.start({
			width:self.size[act].x,
			height:self.size[act].y
		});
		self.slide[act].slideOut();//スライドアウト
	})
	.chain(function(){
		this.element.width = self.size[act].x;
		this.element.height = self.size[act].y;
		this.element.src = this.element.src.replace("_b.","_s.");
		self.morph[act].start({'opacity':1});
		self.lists[act].setStyle("cursor","pointer");
	});
}

これで一応拡大縮小ができました。

【補足】アニメーションのキャンセルとチェイン

7の時点で、クリックしたら拡大他はクローズという動きは完成しているが、
連続してクリックしたり、拡大している途中で別の要素をクリックすると、透明度変更をすっ飛ばして縮小や拡大をしてしまう。
これはFxクラスのlinkオプションがデフォルトでignoreになっているためで、
エフェクト実行中に呼び出されたものはすべて無視される。
そこで、initializeのFx.MorphとFx.Slideにlinkオプションをchainで追加すれば、
キャンセルされることなく実行中のエフェクトが終ってから実行されるようになる。

そのままでいい場合は追加しなくても良い。(お手本サイトはデフォルトのまま)

self.morph[index]= new Fx.Morph(image,{
	duration: self.options.thumbDration,
	transition: self.options.tumbTransition,
	link:"chain"
});

Step8: スクロール

openした要素にスクロールさせる。使うのはFx.Scroll。
Fx.Scrollはoverflow値を持ったあらゆる要素(window含む)をスクロールさせるクラスで、SmoothScrollとは別物。

まずinitializeでFx.Scrollのインスタンスを変数this.Scrollerに格納。対象はwindowオブジェクト

this.Scroller = new Fx.Scroll(window, {dration:this.options.winDration, transition:this.options.winTransition});

インスタンスを作ったらあとはstart()させるだけ。
スクローラーのstartメソッドの引数はx軸座標とy軸座標の値。
CSSのpositionプロパティと同じなので、Elementから取得する場合はgetPositionやgetTopを使う。

まず、アクティブな要素かどうかチェックしているif文のelse。

if(this.active != null){
	this.close(el, this.active);
}else{
	this.Scroller.start(0,el.getTop());//スクロール
}

次に、close()にある最後のchain。

https://tenderfeel.xsrv.jp/wtn/wp-admin/post.php?action=edit&post=678&message=7
close:function(el, act){
	var self = this;
	
	this.morph[act].start({'opacity':0.3})
	.chain(function(){
		this.start({
			width:self.size[act].x,
			height:self.size[act].y
		});
		self.slide[act].slideOut();//スライドアウト
	})
	.chain(function(){
		this.element.width = self.size[act].x;
		this.element.height = self.size[act].y;
		this.element.src = this.element.src.replace("_b.","_s.");
		self.morph[act].start({'opacity':1});
		self.lists[act].setStyle("cursor","pointer");
		self.Scroller.start(0,el.getTop());//スクロール
	});
}

以上で完成!あとは好みの動きになるよう適当に変更すればおk。

ここまでのサンプル

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください