Яндекс.Метрика

Дизайн-журнал №1. Актуальная информация для дизайнеров, веб дизайнеров, программистов и разработчиков сайтов.

Галерея с css3-переходами

11 июля 2013 | Опубликовано в css | 7 Комментариев »

В этом уроке мы будем создавать галерею, в которой с помощью CSS3- переходов реализуется эффект диагонального угасания фотографий. Мы будем сканировать на сервере папку с фотографиями  и отображать их в полный размер экрана в виде сетки. Добавить новую фотографию можно просто скопировав два файла в папку галереи (предварительно отредактировав размеры изображения и превьюшки, для которой желательный размер 150 на 150 пикселей. В браузерах, которые не поддерживают соответствующие свойства css, анимация галереи будет происходить немного по-другому. 

 

Демо                        Скачать исходные файлы

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, которую объявляем перед закрытием тега  body. Также я добавил небольшой лайтбокс -плагин Swipebox, но его можно заменить на любой другой  лайтбокс. Два основных блока #loading и #gallery. При чем первый отвечает за загрузку GIF, второй собственно за саму галерею. Для блока #gallery устанавливаем , так как нам нужно, чтобы галерея занимала всю ширину и высоту страницы.  Разметка для самих фотографий также не сложная:

<a href="assets/photos/large/34.jpg" class="swipebox static" style="width:148px;height:129px;background-image:url(assets/photos/thumbs/34.jpg)">
</a>

Фотографии в  галереи имеют размер 150×150 px. При изменении размера окна браузера в стилях будет для фото определять новые атрибуты ширины и высоты. В разделе JS высможете увидеть.к ак мы будем их рассчитывать.

PHP-сканирование фото

Фотографии хранятся в двух папках - ssets/photos/thumbs/ для превьюшек и ssets/photos/large/ для полных размеров.  С помощью 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,
));

 

Добавлять новые файлы очень просто. Для этого просто нужно скопировать нужные файлы в нужную папку( файлы с полным размером фото и превью должны иметь одинаковые  имена). В примере вы видите, что фотографии дублированы по несколько раз. Скорее всего, вы захотите это изменить и обновить собственные фото.

Давайте перейдем к  JavaScript!

JavaScript

Давайте рассмотрим, что мы будем делать:

  • 1. Определяем AJAX GET-запрос  на извлечение фотографий на диск с кода php.
  • 2. Подсчитываем нужное количество фотографий, чтобы показать их на странице. Количество и размеры фото будут зависеть от размеров окна.
  • 3. Подгружаем фотографии на страницу с помощью скрипта preloader script, который использует  jQuery deferreds. В это время отображается блок #loading.
  • 4. После того, как все файлы будут загружены, сгенерируем разметку для фото и добавим их к элементу #gallery. Потом мы будем генерировать эффект диагональной анимации и инициализировать  Swipebox-галерею.
  • 5. При нажатии пользователем на стрелку шаги 3 и 4 будут повторяться.

Код достаточно большой, чтобы показать его полностью. Поэтому давайте его рассмотри по частям. В данном случае смотрим на общую структуру кода.

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, их для  прозрачности с помощью css определяется значение 0, что создает основу для функций анимации, первая из которых показывает фото без анимации, а две последние создают анимацию волны, начиная с верхней левой картинки, либо с нижней правой. Анимация на основе 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;
}

Они хотя и имеют по несколько строк и используются только раз или два, но позволяют нашему коду быть более читабельным.

Diagonal Fade Effect
Диагональный эффект погасания

CSS

Наконец, приступим к css. Фотографии по дефолту имеют значение непрозрачности — 0, и transform: scale со значение 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()запрещает анимацию (за исключение непрозрачности) и сразу же показывает фотографию( иначе при изменении размера овна вы бы видели все тот же эффект диагонального угасания). Остальную часть кода вы сможете скачать и посмотреть в исходниках к этому уроку.

Автор - Martin Angelov

Перевод — Дежурка

 




Комментарии

  1. Дима
    Thumb up Thumb down 0

    :P

  2. Евгений
    Thumb up Thumb down 0

    Круто парни! Помогли!!!!!!!!!!!!

  3. Виталий
    Thumb up Thumb down 0

    Залил на хостинг и не работает.. TypeError: $.loadImage is not a function. На Денвере работает. В чем может быть причина??

    Андрей Ответ:

    Thumb up Thumb down 0

    А не перепутал нигде большие и маленькие символы?

    Виталий Ответ:

    Thumb up Thumb down 0

    Вот оно что «михалыч», спасибо.. действительно при загрузке на хостинг, у меня стояла голочка — перевести имена в нижний регистр... перезалил без нее и все норм... спс.. *YAHOO*

    Андрей Ответ:

    Thumb up Thumb down 0

    В качестве дебугера использую Мозилу с модулем дебугера, очень удобно.