このチュートリアルでは、css3 トランジションを使用して、滑らかな対角フェード効果を持つギャラリーを作成します。サーバー上の写真を含むフォルダーをスキャンし、ブラウザー ウィンドウ全体にグリッドで表示します。新しい写真の追加は、2 つのファイル (通常の画像とサムネイル、できれば 150x150 ピクセル) をギャラリー フォルダーにコピーするのと同じくらい簡単です。
CSS3 対応のブラウザーは、滑らかにアニメーション化された斜めのフェード効果を表示しますが、古いブラウザーは、ギャラリーのよりシンプルだが完全に使用可能な非アニメーション化バージョンにフォールバックします。
HTML
いつものように、新しいプロジェクトの作業を開始するときに最初に行うことは、HTML を記述することです。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Smooth Diagonal Fade Gallery with CSS3 Transitions</title>
<!-- The Swipebox plugin -->
<link href="assets/swipebox/swipebox.css" rel="stylesheet" />
<!-- The main CSS file -->
<link href="assets/css/style.css" rel="stylesheet" />
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div id="loading"></div>
<div id="gallery"></div>
<!-- JavaScript Includes -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<script src="assets/swipebox/jquery.swipebox.min.js"></script>
<script src="assets/js/jquery.loadImage.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>
ギャラリーは、ボディの終了タグの前に含めた jQuery ライブラリに依存します。 Swipebox と呼ばれる小さなライトボックス プラグインも追加しましたが、お好みのライトボックスに簡単に置き換えることができます。 2 つの主な div は #loading です と #gallery . 1 つ目は読み込み中の gif を保持し、2 つ目はギャラリーの写真を保持します。 #gallery div は position:fixed
に設定されています そのため、ページの幅と高さ全体が使用されます。写真自体のマークアップも同様に単純化されています:
<a href="assets/photos/large/34.jpg" class="swipebox static"
style="width:148px;height:129px;background-image:url(assets/photos/thumbs/34.jpg)">
</a>
ギャラリー内の写真はすべて 150x150 ピクセルです。つまり、サイズを少し変更しない限り、ページ全体に完全に収まることはほとんどありません。これはまさに上の写真に起こったことであり、スタイル属性に幅と高さの値があるのはそのためです。これをどのように計算するかは、JS セクションで確認できます。

PHP による写真のスキャン
写真はサーバー上の 2 つのフォルダー - assets/photos/thumbs/
に含まれています。 サムネイル用、および assets/photos/large/
1 つはフルサイズ用です。 PHP では、フォルダーをスキャンし、ファイル名を含む JSON を出力します。または、データベースから画像を返すこともできますが、同じ構造を維持する必要があります。スクリプトは次のとおりです:
load.php
// Scan all the photos in the folder
$files = glob('assets/photos/large/*.jpg');
$data = array();
foreach($files as $f){
$data[] = array(
'thumb' => str_replace('large', 'thumbs', $f),
'large' => $f
);
}
// Duplicate the photos a few times, so that we have what to paginate in the demo.
// You most certainly wouldn't want to do this with your real photos.
// $data = array_merge($data, $data);
// $data = array_merge($data, $data);
// $data = array_merge($data, $data);
header('Content-type: application/json');
echo json_encode(array(
'data' => $data,
));
新しい写真をギャラリーに追加するのは、画像とそのサムネイルを正しいフォルダーにコピーするのと同じくらい簡単です (両方のファイルは同じ名前にする必要があります!)。写真を数回複製したので、ギャラリーに表示するプールが大きくなりましたが、実際の写真でこれを行うことはおそらくないでしょう.
JSON が用意できたので、JavaScript を書きましょう!
JavaScript
必要なことは次のとおりです。
<オール>コードが長くて一度に紹介できないので、分割して紹介します。まず、全体的な構造は次のとおりです。
assets/js/script.js
$(function(){
// Global variables that hold state
var page = 0,
per_page = 100,
photo_default_size = 150,
picture_width = photo_default_size,
picture_height = photo_default_size,
max_w_photos, max_h_photos
data = [];
// Global variables that cache selectors
var win = $(window),
loading = $('#loading'),
gallery = $('#gallery');
gallery.on('data-ready window-resized page-turned', function(event, direction){
// Here we will have the JavaScript that preloads the images
// and adds them to the gallery
});
// Fetch all the available images with
// a GET AJAX request on load
$.get('load.php', function(response){
// response.data holds the photos
data = response.data;
// Trigger our custom data-ready event
gallery.trigger('data-ready');
});
gallery.on('loading',function(){
// show the preloader
loading.show();
});
gallery.on('loading-finished',function(){
// hide the preloader
loading.hide();
});
gallery.on('click', '.next', function(){
page++;
gallery.trigger('page-turned',['br']);
});
gallery.on('click', '.prev', function(){
page--;
gallery.trigger('page-turned',['tl']);
});
win.on('resize', function(e){
// Here we will monitor the resizing of the window
// and will recalculate how many pictures we can show
// at once and what their sizes should be so they fit perfectly
}).resize();
/* Animation functions */
function show_photos_static(){
// This function will show the images without any animations
}
function show_photos_with_animation_tl(){
// This one will animate the images from the top-left
}
function show_photos_with_animation_br(){
// This one will animate the images from the bottom-right
}
/* Helper functions */
function get_per_page(){
// Here we will calculate how many pictures
// should be shown on current page
}
function get_page_start(p){
// This function will tell us which is the first
// photo that we will have to show on the given page
}
function is_next_page(){
// Should we show the next arrow?
}
function is_prev_page(){
// Should we show the previous arrow?
}
});
一部の関数定義は空白のままですが、ページの下の方に表示されます。変数定義の最初のグループは、ギャラリーの状態 (ディメンション、画像の配列、現在のページなど) を保持します。これにより、ロジックとデータをより明確に分離できます。 (任意の名前付きイベントをリッスンしてトリガーすることにより) コード構成を改善するために、カスタム イベントを使用します。これらのイベント リスナーは、オブジェクトのメソッドと見なすことができ、先頭付近の変数はそのプロパティと見なすことができます。
上記のフラグメントのすべてのコメントを読んだら、最初のイベント リスナーに進みます。このイベント リスナーは、現在のページに応じて画像配列の関連するスライスを出力します。
gallery.on('data-ready window-resized page-turned', function(event, direction){
var cache = [],
deferreds = [];
gallery.trigger('loading');
// The photos that we should be showing on the new screen
var set = data.slice(get_page_start(), get_page_start() + get_per_page());
$.each(set, function(){
// Create a deferred for each image, so
// we know when they are all loaded
deferreds.push($.loadImage(this.thumb));
// build the cache
cache.push('<a href="' + this.large + '" class="swipebox"' +
'style="width:' + picture_width + 'px;height:' + picture_height + 'px;background-image:url(' + this.thumb + ')">'+
'</a>');
});
if(is_prev_page()){
cache.unshift('<a class="prev" style="width:' + picture_width + 'px;height:' + picture_height + 'px;"></a>');
}
if(is_next_page()){
cache.push('<a class="next" style="width:' + picture_width + 'px;height:' + picture_height + 'px;"></a>');
}
if(!cache.length){
// There aren't any images
return false;
}
// Call the $.when() function using apply, so that
// the deferreds array is passed as individual arguments.
// $.when(arg1, arg2) is the same as $.when.apply($, [arg1, arg2])
$.when.apply($, deferreds).always(function(){
// All images have been loaded!
if(event.type == 'window-resized'){
// No need to animate the photos
// if this is a resize event
gallery.html(cache.join(''));
show_photos_static();
// Re-initialize the swipebox
$('#gallery .swipebox').swipebox();
}
else{
// Create a fade out effect
gallery.fadeOut(function(){
// Add the photos to the gallery
gallery.html(cache.join(''));
if(event.type == 'page-turned' && direction == 'br'){
show_photos_with_animation_br();
}
else{
show_photos_with_animation_tl();
}
// Re-initialize the swipebox
$('#gallery .swipebox').swipebox();
gallery.show();
});
}
gallery.trigger('loading-finished');
});
});
画像は #gallery に追加されますが 単一の操作で div を作成すると、opacity:0
に設定されます cssで。これにより、アニメーション機能の準備が整います。前者はアニメーションなしの写真を表示し、後者の 2 つは左上または右下から波状にアニメーション化します。アニメーションは完全に CSS ベースであり、jQuery で画像にクラス名を割り当てるとトリガーされます。
function show_photos_static(){
// Show the images without any animations
gallery.find('a').addClass('static');
}
function show_photos_with_animation_tl(){
// Animate the images from the top-left
var photos = gallery.find('a');
for(var i=0; i<max_w_photos + max_h_photos; i++){
var j = i;
// Loop through all the lines
for(var l = 0; l < max_h_photos; l++){
// If the photo is not of the current line, stop.
if(j < l*max_w_photos) break;
// Schedule a timeout. It is wrapped in an anonymous
// function to preserve the value of the j variable
(function(j){
setTimeout(function(){
photos.eq(j).addClass('show');
}, i*50);
})(j);
// Increment the counter so it points to the photo
// to the left on the line below
j += max_w_photos - 1;
}
}
}
function show_photos_with_animation_br(){
// Animate the images from the bottom-right
var photos = gallery.find('a');
for(var i=0; i<max_w_photos + max_h_photos; i++){
var j = per_page - i;
// Loop through all the lines
for(var l = max_h_photos-1; l >= 0; l--){
// If the photo is not of the current line, stop.
if(j > (l+1)*max_w_photos-1) break;
// Schedule a timeout. It is wrapped in an anonymous
// function to preserve the value of the j variable
(function(j){
setTimeout(function(){
photos.eq(j).addClass('show');
}, i*50);
})(j);
// Decrement the counter so it points to the photo
// to the right on the line above
j -= max_w_photos - 1;
}
}
}
次は、ウィンドウのサイズ変更イベントをリッスンする関数です。これは、ブラウザー ウィンドウのサイズが変更されたとき、またはデバイスの向きが変更されたときに発生する可能性があります。この関数では、画面に収まる写真の数と、完全に収まるように正確なサイズを計算します。
win.on('resize', function(e){
var width = win.width(),
height = win.height(),
gallery_width, gallery_height,
difference;
// How many photos can we fit on one line?
max_w_photos = Math.ceil(width/photo_default_size);
// Difference holds how much we should shrink each of the photos
difference = (max_w_photos * photo_default_size - width) / max_w_photos;
// Set the global width variable of the pictures.
picture_width = Math.ceil(photo_default_size - difference);
// Set the gallery width
gallery_width = max_w_photos * picture_width;
// Let's do the same with the height:
max_h_photos = Math.ceil(height/photo_default_size);
difference = (max_h_photos * photo_default_size - height) / max_h_photos;
picture_height = Math.ceil(photo_default_size - difference);
gallery_height = max_h_photos * picture_height;
// How many photos to show per page?
per_page = max_w_photos*max_h_photos;
// Resize the gallery holder
gallery.width(gallery_width).height(gallery_height);
gallery.trigger('window-resized');
}).resize();
最後の行では、関数が定義された直後にトリガーされます。これは、最初から正しい値があることを意味します。
次のヘルパー関数は、最も頻繁に使用される計算の一部を抽象化します:
function get_per_page(){
// How many pictures should be shown on current page
// The first page has only one arrow,
// so we decrease the per_page argument with 1
if(page == 0){
return per_page - 1;
}
// Is this the last page?
if(get_page_start() + per_page - 1 > data.length - 1){
// It also has 1 arrow.
return per_page - 1;
}
// The other pages have two arrows.
return per_page - 2;
}
function get_page_start(p){
// Which position holds the first photo
// that is to be shown on the give page
if(p === undefined){
p = page;
}
if(p == 0){
return 0;
}
// (per_page - 2) because the arrows take up two places for photos
// + 1 at the end because the first page has only a next arrow.
return (per_page - 2)*p + 1;
}
function is_next_page(){
// Should we show the next arrow?
return data.length > get_page_start(page + 1);
}
function is_prev_page(){
// Should we show the previous arrow?
return page > 0;
}
それらはほんの数行の長さで、1 回か 2 回しか使用されないかもしれませんが、コードを読みやすくするために大いに役立ちます。

CSS
最後にCSSコードです。デフォルトでは、写真の不透明度はゼロで、0.8 のスケール変換が適用されています。また、トランジション プロパティ セットもあり、属性のすべての変更がアニメーション化されます。 .show アニメーション関数によって追加されるクラスは、ブラウザーによって自動的にアニメーション化される要素の不透明度とスケールを上げます。
assets/css/styles.css
#gallery{
position:fixed;
top:0;
left:0;
width:100%;
height:100%;
}
#gallery a{
opacity:0;
float:left;
background-size:cover;
background-position: center center;
-webkit-transform:scale(0.8);
-moz-transform:scale(0.8);
transform:scale(0.8);
-webkit-transition:0.4s;
-moz-transition:0.4s;
transition:0.4s;
}
#gallery a.static:hover,
#gallery a.show:hover{
opacity:0.9 !important;
}
#gallery a.static{
opacity:1;
-webkit-transform:none;
-moz-transform:none;
transform:none;
-webkit-transition:opacity 0.4s;
-moz-transition:opacity 0.4s;
transition:opacity 0.4s;
}
#gallery a.next,
#gallery a.prev{
background-color:#333;
cursor:pointer;
}
#gallery a.next{
background-image:url('../img/arrow_next.jpg');
}
#gallery a.prev{
background-image:url('../img/arrow_prev.jpg');
}
#gallery a.show{
opacity:1;
-webkit-transform:scale(1);
-moz-transform:scale(1);
transform:scale(1);
}
.static クラスは show_photos_static()
によって設定されます 関数であり、すべてのアニメーションを無効にし (ホバー効果を滑らかにしたいので不透明度を除く)、写真をすぐに表示します (そうしないと、サイズ変更のたびに斜めのフェードが表示されます)。このファイルの残りの部分は、ページの上部にあるボタンからダウンロードできるチュートリアル ファイルで確認できます。
これで完了です!
この小さな実験を気に入っていただき、この滑らかにアニメーション化されたギャラリーを多くの用途で見つけていただければ幸いです。