八ヶ岳 赤岳、阿弥陀岳を望む(権現岳)


Date/Time: 2013:10:13 08:24:13
Camera: RICOH
Model: GR DIGITAL 4
Exporsure Time: 1/320
FNumber: 4.5
Aperture Value: 4.3
Focal Length: 6.0

Close

y2blog » Galleria V1.2 を試す(その3)

11

26

2010

Galleria V1.2 を試す(その3)

カスタムテーマによる機能拡張


前回は新しくなった Galleria V1.2 でHTMLソース内のオプションで拡張する方法について簡単に紹介しましたが、今回はカスタムテーマファイルを用いてGalleria V1.2の機能を拡張する方法について説明します.


Galleria V1.2 のソースコードをダウンロードすると、themes フォルダの中に2つのテーマファイル群 “classic”, “fullscreen” が用意されていますが、今回は一般的な “classic” テーマを用いた機能拡張について説明して行きたいと思います.

カスタムテーマファイルの中身


ユーザがカスタムテーマファイルを作成する際に必要になる基本的なファイルは、jQuery ベースのJavascriptファイルとそのテーマに関連するCSSファイルの2つです.”classic” の場合、 “galleria.classic.js” と “galleria.classic.css” のファイルと関係するアイコンなどのリソースが含まれています.


Galleriaが起動される際に、その初期化処理の途中でユーザが指定したJavascriptのテーマファイルがWEBブラウザのDOMオブジェクトに組み込まれます.テーマファイルを指定するにはHTMLソースコードの中で、
Galleria.loadTheme(‘./themes/classic/galleria.classic.js’);
のように指定します.この記述はGalleriaが初期化(起動)される前に行う必要がありますので、 jQuery().galleria({}); よりも前に記述して下さい.


組み合わせるCSSファイルは、Javascript テーマファイルの最初の方に指定する箇所がありますので自分のテーマに合わせて適切なCSSファイルを指定して下さい.Galleria V1.2 では “loadTheme” メソッド内で、指定されたJavascriptファイルを DOM操作によってdocument オブジェクトに直接組み込んでいます.ファイル名の命名規則とテーマファイルの名前には関連付けが有るようですので、標準の命名規則に沿って付けて下さい.


HTML中で Galleria.loadTheme(‘…’); を記述する代わりに、<head> エレメント内に直接 <script> を用いて組み込んでも構いません.


オリジナル classic テーマファイルの内容 [sourcecode language=”javascript” wraplines=”true”] /* * Galleria Classic Theme v. 1.5 2010-10-28 * http://galleria.aino.se * * Copyright (c) 2010, Aino * Licensed under the MIT license. */ (function($) { Galleria.addTheme({ name: ‘classic’, author: ‘Galleria’, version: ‘1.5’, css: ‘galleria.classic.css’, defaults: { transition: ‘slide’, thumb_crop: ‘height’, // set this to false if you want to show the caption all the time: _toggle_info: true }, init: function(options) { // add some elements this.addElement(‘info-link’,’info-close’); this.append({ ‘info’ : [‘info-link’,’info-close’] }); // cache some stuff var toggle = this.$(‘image-nav-left,image-nav-right,counter’), info = this.$(‘info-link,info-close,info-text’), click = Galleria.TOUCH ? ‘touchstart’ : ‘click’; // show loader & counter with opacity this.$(‘loader,counter’).show().css(‘opacity’,.4) // some stuff for non-touch browsers if (! Galleria.TOUCH ) { // fade thumbnails this.$(‘thumbnails’).children().hover(function() { $(this).not(‘.active’).children().stop().fadeTo(100, 1); }, function() { $(this).not(‘.active’).children().stop().fadeTo(400, .6); }); this.addIdleState( this.get(‘image-nav-left’), { left:-50 }); this.addIdleState( this.get(‘image-nav-right’), { right:-50 }); this.addIdleState( this.get(‘counter’), { opacity:0 }); } // toggle info if ( options._toggle_info ) { info.bind( click, function() { info.toggle(); }); } // bind some stuff this.bind(Galleria.THUMBNAIL, function(e) { $(e.thumbTarget).parent(‘:not(.active)’).children().css(‘opacity’,.6) }); this.bind(Galleria.LOADSTART, function(e) { if (!e.cached) { this.$(‘loader’).show().fadeTo(200, .4); } this.$(‘info’).toggle( this.hasInfo() ); $(e.thumbTarget).css(‘opacity’,1).parent().siblings().children().css(‘opacity’,.6); }); this.bind(Galleria.LOADFINISH, function(e) { this.$(‘loader’).fadeOut(200); }); } }); })(jQuery); [/sourcecode]


このjQueryベースのソースコードを眺めても今一つピンと来ないかもしれませんが、主な処理内容は次のようになります.


・デフォルトのオプション設定( 16〜22行目 )

あまり変更することのないオプションはここで設定しておくのが良いでしょう
・DOMエレメントの追加処理( 25〜29行目 )

Galleria が標準で使うDOMエレメントは、メインスクリプトファイル “galleria.js” の中で生成されますが、ユーザが独自に<div>エレメントを追加したい場合はここに処理を記述します.

addElement, append というメソッドが用意されているようです.

ここでは、info-link, info-close というクラスの div エレメントを2つ infoクラスの<div>エレメントの配下に追加しています.


ギャラリー上では、”i” のイタリック文字と “X” のクローズボックスに割り当てられています.

尚、実際のDOMエレメントの名前はユーザが指定した文字列の前に “galleria-” というプリフィクス文字列が追加されますので、

最終的なDOMのエレメントは次のようになるでしょう.


<div class=”galleria-info”>

  <div class=”galleria-info-text”>

    <div class=”galleria-info-title”>

    <div class=”galleria-info-description”>

    <div class=”galleria-info-author”>

  <div class=”galleria-info-link”>  ← 追加

  <div class=”galleria-info-close”>  ← 追加

</div>
・画像のインデクスカウンターやナビゲーション矢印、サムネイル画像の透明度の設定( 36〜52行目 )

画像が表示されてからアイドル状態に遷移する際のアニメーション効果などをここで設定している.


・画像情報の表示機能の ON/OFF をマウスクリックに結びつけて制御する設定( 54〜59行目 )

画像に “title”, “alt” 属性が含まれているとギャラリー上にイタリック体の”i” 文字が表示されて、

そこをクリックすると”title”, “alt” 属性の内容が表示される.クローズボックスのクリックで表示 OFF となる.


・サムネイル画像のアクティブ/非アクティブ要素の透明度の(明暗)設定( 62〜64行目、73行目 )


・ターゲットの画像のロード中にローダーアイコン画像(ぐるぐる)を表示( 68行目 )
・ターゲットの画像の表示後にローダーアイコン画像を非表示にする( 77行目 )


カスタムテーマファイルでどの様な処理が可能なのか


基本的にはユーザが自分で機能を追加しGalleriaの枠組みの中に組み込めば何でも可能でしょう.”classic” テーマファイルでは予め用意されているイベントのハンドリング機能のうちの一部しか使っていません.


テーマファイルの中でハンドリング可能なイベントについては、 http://galleria.aino.se/docs/1.2/api/events/ に詳細が記述されています.


“classic” テーマでは ”THUMBNAIL”, “LOADSTART”, “LOADFINISH” の3つしか使っていませんが、”IMAGE”, “IDLE_ENTER” など便利なイベントトリガーが幾つか用意されています.


前回の記事でHTMLソースコード中のオプション拡張機能の “on_image” について紹介しましたが、全く同じ機能が “IMAGE”トリガーを使って行うことが可能です.下記の例のように、自分でイベントハンドラーを追加して下さい.


		this.bind(Galleria.IMAGE, function(e) {
			// 
			// same as "on_image" option
			//
  			 jQuery(e.imageTarget).dblclick( function() {
                                  //	do something ...		 	
                                  //	画像をダブルクリックしたら何かを行う...		 	
  			 } );
		});

		this.bind(Galleria.IDLE_ENTER, function(e) {
                        // show image counter
                        // アイドル状態に入ったら画像カウンターの透明度を下げる
			this.jQuery('counter').css('opacity',0.4).show();
		});


“PLAY”, “PAUSE” などの自動スライドショーのトリガーや、”LIGHTBOX_IMAGE” のような Galleriaに標準で組み込まれているLightbox 表示機能のイベントトリガーなども用意されています.


“classic” テーマの拡張例


“classic” テーマを拡張したサンプルコードを例として示します.次に示す例は前回の記事でデモをしたサンプルのものです.


ここでは画像情報の表示部分をカスタマイズして、<img>タグのtitle属性の内容と、自分で取得した画像のEXIFタグ情報を表示するようにしています.


メインステージ上に画像の表示が完了した時点で呼び出される “LOADFINISH” トリガーのハンドラー内で画像のEXIFタグ情報を取得して、画像情報表示エリアの <div class=”.galleria-info-descrption”>タグ、<div class=”.galleria-info-author”>タグのテキストを強制書き換えしています.


<div class=”.galleria-info-title”>タグにはそのまま title属性の値が設定されていますが、alt属性の値は<div class=”.galleria-info-descrption”>タグに設定されます.DOMエレメントとして<div class=”.galleria-info-author”>タグもあるのですが、こちらには何も値が設定されないようなので勝手にこのエレメントを利用させて貰うことにしました.


メインステージ上に画像が表示されている際に、title属性とalt属性が<img>タグに記述されていないのはHTML的に気持ち悪いので、無理矢理title属性とalt属性を書き加えています.


書き換えをする際に、単純に jQuery(‘.galleria-info-descrption’).html(“…”); のようにすると同一ページ内にギャラリーが複数有る場合、別なギャラリー上の項目も含まれてしまいますので、対象となる画像オブジェクトのDOMエレメントからその親DOMエレメントを辿り、目的のDOMエレメントまで延々と辿るという不器用な方法を使っています.


多分もっと簡潔でスムースな jQueryのセレクター指定方法があるのかもしれませんが、何せJavascriptやjQueryについては初心者レベルなので、この辺のいい加減な解決方法についてはお許し下さい.m(__)m
【補足(2/3 2011): やはり目的のターゲットへ直接アクセスする方法がありました】

Galleria オブジェクト “this” を使えば自分で目的のターゲットを探す必要はありませんね.(^_^;)
this.jQuery( ‘stage’ ) のように指定すれば、目的のターゲットの jQuery オブジェクトが得られます.ちなみに、 this.get( ‘stage’ ) のように get() メソッドを使うと 目的の HTML エレメントをダイレクトに取り出せます.


尚、Galleria V1.2 の本体スクリプト “galleria.js” には手を入れない予定でしたが、デフォルトの DOMエレメントの構成ではちょっと不都合があったので、ほんの少しだけ手を入れてGalleriaが作成する DOMエレメントの構成(イメージカウンターの配置)を変更してあります.テーマファイル側では本体スクリプト “galleria.js” の変更による影響はありませんが、対応するCSSファイル側では画面配置などを大きく変更しています.

カスタマイズしたテーマファイルの例 https://y2tech.net/galleria_test/themes/classic/galleria.myclassic.js [sourcecode language=”javascript” wraplines=”true”] /* * Galleria Classic Theme v. 1.5 2010-10-28 * http://galleria.aino.se * * Copyright (c) 2010, Aino * Licensed under the MIT license. */ /*======================================================================*/ /* galleria.myclassic.js : A sample Galleria theme extention script */ /* */ /* Modified by Y2 Nov 23rd 2010 */ /*======================================================================*/ (function($) { Galleria.addTheme({ name: ‘myclassic’, author: ‘Y2’, version: ‘1.0’, css: ‘galleria.myclassic.css’, defaults: { transition: ‘slide’, thumb_crop: ‘height’, thumb_margin : 2, // set this to false if you want to show the caption all the time: _toggle_info: true }, init: function(options) { // add some elements this.addElement(‘info-link’,’info-close’); this.append({ ‘info’ : [‘info-link’,’info-close’] }); // cache some stuff var toggle = this.jQuery(‘image-nav-left,image-nav-right,counter’), info = this.jQuery(‘info-link,info-close,info-text’), click = Galleria.TOUCH ? ‘touchstart’ : ‘click’; // show loader & counter with opacity this.jQuery(‘loader,counter’).show().css(‘opacity’,1.0) // some stuff for non-touch browsers if (! Galleria.TOUCH ) { // fade thumbnails this.jQuery(‘thumbnails’).children().hover(function() { jQuery(this).not(‘.active’).children().stop().fadeTo(100, 1.0); }, function() { jQuery(this).not(‘.active’).children().stop().fadeTo(400, 0.6); }); this.addIdleState( this.get(‘image-nav-left’), { left:-50 }); this.addIdleState( this.get(‘image-nav-right’), { right:-50 }); this.addIdleState( this.get(‘counter’), { opacity:0.4 }); } // toggle info if ( options._toggle_info ) { info.bind( click, function() { info.toggle(); }); } // initialy force to show the information text field info.toggle(); // bind some stuff this.bind(Galleria.THUMBNAIL, function(e) { jQuery(e.thumbTarget).parent(‘:not(.active)’).children().css(‘opacity’,0.4); }); this.bind(Galleria.LOADSTART, function(e) { if (!e.cached) { this.jQuery(‘loader’).show().fadeTo(200, 1.0); } this.jQuery(‘info’).toggle( this.hasInfo() ); jQuery(e.thumbTarget).css(‘opacity’,1).parent().siblings().children().css(‘opacity’,0.4); }); this.bind(Galleria.LOADFINISH, function(e) { // retrieve original image atributes jQuery(e.imageTarget).attr( "title", this.getData(e.index).title ); jQuery(e.imageTarget).attr( "alt", this.getData(e.index).description ); // try to get some EXIF Tag data EXIF.getData( e.imageTarget, function() { var theDateTime = EXIF.getTag( e.imageTarget, "DateTimeOriginal" ); var theMake = EXIF.getTag( e.imageTarget, "Make" ); var theModel = EXIF.getTag( e.imageTarget, "Model" ); var theApertureValue = EXIF.getTag( e.imageTarget, "ApertureValue" ); var theExposureTime = EXIF.getTag( e.imageTarget, "ExposureTime" ); var theISOSpeedRatings = EXIF.getTag( e.imageTarget, "ISOSpeedRatings" ); var theExposureBias = EXIF.getTag( e.imageTarget, "ExposureBias" ); var theFocalLength = EXIF.getTag( e.imageTarget, "FocalLength" ); var theFocalLengthIn35 = EXIF.getTag( e.imageTarget, "FocalLengthIn35mmFilm" ); var theExposureMode = EXIF.getTag( e.imageTarget, "ExposureMode" ); var theSubjectDistanceRange = EXIF.getTag( e.imageTarget, "SubjectDistanceRange" ); var theGPSLatitude = EXIF.getTag( e.imageTarget, "GPSLatitude" ); var theGPSLatitudeRef = EXIF.getTag( e.imageTarget, "GPSLatitudeRef" ); var theGPSLongitude = EXIF.getTag( e.imageTarget, "GPSLongitude" ); var theGPSLongitudeRef = EXIF.getTag( e.imageTarget, "GPSLongitudeRef" ); var theGPSAltitude = EXIF.getTag( e.imageTarget, "GPSAltitude" ); var theGPSAltitudeRef = EXIF.getTag( e.imageTarget, "GPSAltitudeRef" ); var theGPSMapDatum = EXIF.getTag( e.imageTarget, "GPSMapDatum" ); var theGPSSatellites = EXIF.getTag( e.imageTarget, "GPSSatellites" ); var theGPSDateStamp = EXIF.getTag( e.imageTarget, "GPSDateStamp" ); var theGPSTimeStamp = EXIF.getTag( e.imageTarget, "GPSTimeStamp" ); var theGPSImgDirection = EXIF.getTag( e.imageTarget, "GPSImgDirection" ); var theGPSImgDirectionRef = EXIF.getTag( e.imageTarget, "GPSImgDirectionRef" ); var descrition = ( theDateTime ? "Date/Time : " + theDateTime : "" ) + ( theGPSLatitude ? " Location : " + ExifGPSValueToDMSString( theGPSLatitude, theGPSLatitudeRef ) : "" ) + ( theGPSLongitude ? ", " + ExifGPSValueToDMSString( theGPSLongitude, theGPSLongitudeRef ) : "" ) + ( theGPSAltitude ? " Altitude : " + ExifGPSAltitudeString( theGPSAltitude, theGPSAltitudeRef ) : "" ) + ( theGPSMapDatum ? " " + theGPSMapDatum : "" ); var exifData = ( ( theModel || theMake ) ? " Model :" + theMake + " " + theModel : "" ) + ( theApertureValue ? " F : " + Math.floor( ( Math.round( theApertureValue * 100 ) ) ) / 100.0 : "" ) + ( theExposureTime ? ", ExposureTime : 1/" + Math.floor( ( Math.round( 1.0/ theExposureTime * 100 ) ) ) / 100.0 + "s" : "" ) + ( theISOSpeedRatings ? " ISO Speed : " + theISOSpeedRatings : "" ) + ( theFocalLength ? " Focal Length : " + theFocalLength + "mm" : "" ) + ( theExposureBias ? " Exporsure Bias : " + theExposureBias : "" ) + ( theGPSImgDirection ? " Direction : " + Math.round( theGPSImgDirection ) + " " + theGPSImgDirectionRef : "" ); // get an inner container div var divContainer = jQuery(e.imageTarget).parent().parent().parent().parent(); // replace ‘info-description’ text if ( descrition) { //divContainer.children(‘div.galleria-info’).children(‘div.galleria-info-text’).children(‘div.galleria-info-description’).html( descrition ); 【 訂正:とてもシンプルな方法がありました (^^ゞ 】 this.jQuery(‘info-description’).html( description ); // <= } // add some EXIF tag data to ‘info-author’ text field if ( exifData ) { //divContainer.children(‘div.galleria-info’).children(‘div.galleria-info-text’).children(‘div.galleria-info-author’).html( exifData ).show(); this.jQuery(‘info-author’).html( exifData ).show(); // <= } } ); this.jQuery(‘loader’).fadeOut(200); }); this.bind(Galleria.IMAGE, function(e) { // same as "on_image" option //jQuery(e.imageTarget).dblclick( function() { // do something … //} ); }); this.bind(Galleria.IDLE_ENTER, function(e) { this.jQuery(‘counter’).show().css(‘opacity’,0.6); }); } }); })(jQuery); [/sourcecode]