Создание jQuery плагина слайд шоу с нуля

Создание jQuery плагина слайд шоу с нуля

От автора: Многие вещи касательно того, как мы подходим к разработке фронт энда, значительно изменились за последние пару лет. У всех нас есть любимые плагины, которые мы используем при необходимости, многие из них были созданы во времена фиксированных сеток, но, возможно, они уже не являются наилучшими инструментами для работы. Что делать нам, когда это происходит? Искать новые инструменты или создавать свои собственные?

демо

Предисловие

Что касается изменений в нашей работе, самым очевидным является подход к каждому проекту как «резиновому», отзывчивому, созданному с нуля. К счастью, у нас есть отличные инструменты, входящие в саму спецификацию CSS, такие как проценты и медиа-запросы, чтобы помочь нам. Но многие из наших любимых плагинов для таких вещей как слайд шоу, карусели, модальные окна, имеют один фатальный недостаток, когда дело доходит до отзывчивой разметки – встроенные стили, добавляемые JavaScript’ом и множество их. Когда мы пытаемся использовать эти плагины в «новом поколении» отзывчивых сайтов, это заканчивается тем, что мы постоянно боремся с этими стилями с хакерскими переопределениями или чем-то похуже, появляется еще больше JavaScript кода, чтобы контролировать поведение и вытеснить старый код.

Что же нам нужно контролировать с помощью JavaScript?

Для меня хороший фронт энд плагин для «отзывчивой эры», должен производить абсолютный минимум встроенных стилей, необходимых для работы, и ничего более. Например, если нам нужен простой плагин слайд-шоу, в котором набор картинок сменяется с эффектом обесцвечивания, что нам действительно нужно контролировать с помощью JavaScript? Если мы на секунду задумаемся над этим, то возможно это будут свойства opacity и z-index, и ничего более. Когда вы видите как слайд шоу генерирует такие вещи, как позиционирование, ширину и высоту и обтекание, наступает время признать, что плагин возможно сослужил свою службу во времена фиксированных сеток, но пришло время двигаться дальше.

Имея это в виду, я задумался над тем, насколько сложно будет создать легковесный плагин слайд шоу с чистого листа – создать что-то легковесное и вместе с тем отзывчивое, только с тем функционалом, который мне нужен, и, что самое важное, оставляет как можно больше возможностей стилизации пользователю.

Я задумался над тем, насколько сложно будет создать легковесный плагин слайд шоу с чистого листа

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

В этой статье, я пошагово проведу вас через этот процесс. Мы будем вдаваться в детали по мере прогресса, и хотя я сделаю все возможное, но вам эта статья будет наиболее полезна, если вы уверенно работаете с jQuery и ее методами, а также имеет опыт написания JavaScript кода с нуля, используя переменные и функции.

Быть ответственным за каждую строчку кода в вашем проекте – это замечательная вещь

Фаза 1: Концептуализация

Прежде чем начать. Мне кажется полезной визуализация того, как плагин будет работать, и какой функционал понадобится. Почему бы не начать с того, чтобы написать список или не сделать быстрый набросок?

Функционал

Плагин будет программно обесцвечиваться сквозь набор абсолютно позиционированных элементов посредством анимирования их непрозрачности.

Слайды могут быть картинками или любыми другими элементами, заключенными в div.

Чтобы убедиться в том, что мы можем перейти от одного слайда к другому (они необязательно могут находиться на одном уровне по отношению к родительскому элементу) нам также нужно изменять значение z-index у них.

Плагин должен использовать свойство CSS3 transition, когда возможно или же jQuery .animate().

Свойства

Автоматически перемещать слайды по порядку, останавливаясь на период времени, определенный пользователем.

Перемещать слайды по клику, используя элементы управления вперед/назад.

Показывать определенный слайд напрямую посредством списка кнопок с классом «pager».

Позволять пользователю конфигурировать интервал таймера, а также интервал обесцвечивания с момента начала показа слайдов.

Фаза 2: Создание (Быстрое и с погрешностями)

Как упоминалось ранее, наилучшей практикой для создания отзывчивых плагинов – это оставить как можно больше стилизации для таблицы стилей, и использовать встроенные стили, добавляемые JavaScript’ом только там, где это точно необходимо. Имея вышесказанное ввиду, давайте начнем с написания HTML и CSS.

<div id="Fader" class="fader">
    <img class="slide" src="..."/>
    <img class="slide" src="..."/>
    <div class="slide"></div>
    <div class="slide"></div>
    ...
</div>

Я представляю, что на самом базовом уровне, разметка нашего слайд шоу будет выглядеть как-то так. Как упоминалось ранее, сами слайды не должны быть ограничены только изображениями, поэтому я добавил несколько div тэгов. Таким образом, наш плагин должен идентифицировать их как слайды благодаря классу “slide”, а не по тэгам или позиции в DOM.

Наш контейнер в идеале должен содержать и класс и идентификатор. Мы будем использовать класс для определения стилей слайд шоу, что позволит нам иметь несколько слайд шоу на одной странице, и мы будем использовать ID, чтобы обращаться к каждому слайд шоу с помощью jQuery уникально.

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

.fader{
    position: relative;
    padding-top: 60%;
    overflow: hidden;
} 
 
.fader .slide{
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    z-index: 1;
    opacity: 0;
}
 
.fader img.slide{
    height: auto;
}

Вместо того, чтобы задавать высоту нашему контейнеру, я предпочел задать значение свойству padding-top. Этот прием позволяет масштабировать высоту элемента на основе его ширины, а не на основе высоты родительского контейнера. Для слайд шоу с фиксированным отношением ширины к высоте, это будет неотъемлемой частью отзывчивого поведения. Я выбрал отношение 5 : 3 или, другими словами, высота составляет 60% ширины.

Задавая всем нашим слайдам z-index равный 1, мы сжимаем естественный порядок слайдов в рамках одинакового значения z-index, будучи уверенными, что они не будут препятствовать 2м верхним сменяющимся слайдам, которые имеют z-index 2 и 3 (смотрите набросок). Задавая всем слайдам непрозрачность равную 0, мы будем контролировать то, какой слайд появится первым, когда нам нужно и избежим так называемой «вспышки нестилизованного контента».

Последний стиль просто переназначает 100% высоту значением “height: auto” если элемент слайда является изображением. Это удостоверяет, что если изображение имеет отношение ширины к высоте, отличное от 5:3, то оно не будет искажаться из-за высоты в 100%.

Теперь когда HTML и CSS в порядке, давайте перейдем к занимательным вещам – jQuery!

Что такое плагин jQuery помимо того, что он является набором переменных и функций? Это то, с чего мне нравится начинать написание плагина.

Что такое плагин jQuery помимо того, что он является набором переменных и функций?

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

Контейнер

Селектор класса слайда

Продолжительность показа одного слайда

Продолжительность обесцвечивания

Количество слайдов

Порядковый номер текущего «активного» слайда

Порядковый номер следующего слайда

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

Контейнер — объект jQuery

Селектор класса слайда — строка

Продолжительность показа одного слайда – число (в миллисекундах)

Продолжительность обесцвечивания – число (в миллисекундах)

Количество слайдов — число

Порядковый номер текущего «активного» слайда — число

Порядковый номер следующего слайда – число

Теперь время объявить нашу самую главную функцию. Я решил назвать ее easyFader, таким же будет и название нашего плагина.

function easyFader(){
 
}

Мы будем создавать слайд шоу на странице посредством вызова этой функции, поэтому нам возможно понадобится передать несколько аргументов этой функции, для того, чтобы наш плагин имел все необходимые данные для начала работы, и пользователь имел возможность конфигурировать каждый экземпляр плагина. Из списка приведенного выше нам необходимо передать следующие данные:jQuery объект контейнера, продолжительность показа одного слайда и длительность обесцвечивания. Все остальное будет программно рассчитано позднее. Мы можем дать пользователю возможность выбрать также селектор слайда, но пока что мы зададим его в коде.

Вот как выглядит объявленная функция вместе с заданными «аргументами»:

function easyFader($container, slideDur, fadeDur){
 
    //Весь наш код будет здесь.
 
}

Запомните: символ $ помогает нам помнить о том, что эта переменная является объектом jQuery.

Таким образом, когда мы вызываем функцию для инициализации слайд шоу, она будет выглядеть так:

easyFader($('#Fader'),7000,800);

Это вызовет плагин на элементе с идентификатором Fader, с длительностью показа слайдов равной 7 секундам, и периодом обесцвечивания в 800 миллисекунд.

Возможно, вы заметили, что синтаксис выглядит как обычная функция JavaScript, а не плагин jQuery, и вы будете абсолютно правы. Я думаю, что во время первого «скорого» прототипирования, лучшим путем является придерживаться того, с чем мы наилучшим образом знакомы, чтобы сохранить время и избежать недопонимания. Мы доберемся до модных приемов плагина позднее.

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

Первым шагом в следующем коде является создание пустого объекта под названием faderConfig и добавление его как свойства к сырому объекту DOM нашего контейнера. Чтобы иметь доступ к этому объекту jQuery мы просто размещаем [0] сразу после объекта jQuery, что равнозначно window.getElementByID(‘Fader’) на чистом JavaScript. Именно там мы будем хранить все четыре общедоступные настройки конфигурации, и привязывая его к DOM, наши данные не будут замкнуты внутри нашей функции, и могут быть доступны и редактируемы извне в любое время, за пределами функции слайд шоу.

Эта маленькая уловка позволит нам иметь несколько экземпляров плагина на одной странице, без того, чтобы вдаваться в более сложные объектно-ориентированные структуры, включающие классы, инстанции и методы, что лежит за пределами этой статьи. Создавая наш плагин как набор функций, а не как объект с методами, процесс будет немного легче для понимания тем, кто пишет свой первый плагин.

На данный момент, время закатать рукава и приняться за работу. Я постараюсь сделать объяснения короче, что позволит комментариям и коду говорить самим за себя.

Инициализация

function easyFader($container, slideDur, fadeDur){
 
    // Добавляем пустой конфигурационный объект
    // DOM объекту контейнера
 
    $container[0].faderConfig = {};
 
    // Кэшируем объект как локальную переменную
 
    var config = $container[0].faderConfig;
 
    // Добавляем общедоступные свойства объекту
 
    config = {
        slideDur : slideDur,
        fadeDur : fadeDur   
    }
 
    // Объявляем приватные переменные. Они будут использоваться для хранения данных, 
// которые не определяются пользoвателем.
 
    var slideSelector = '.slide', // selector for target elements
        slideTimer, // will contain our setTimeout function
        totalSlides, // total number of slides
        activeSlide, // index of active slide
        newSlide, // index of upcoming slide
        $slides = $container.find(slideSelector);
        // jQuery объект, содержащий все слайды
 
    // Find the total number of slides
 
    totalSlides = $slides.length;

По умолчанию, последний слайд в DOM будет иметь самое высокое значение z-index и таким образом будет показываться первым, даже если все наши слайды приобретут opacity:1. Однако, мы хотим, чтоб первый слайд показывался первым:

// Показать первый слайд используя метод jQuery eq()
 
    $slides.eq(0).css('opacity',1);
 
    // Теперь наш первый слайд активен 
    // давайте зададим переменной activeSlide значение 0 
    // (Запомните: в JavaScript, счет начинается с 0, а не с 1)
 
    activeSlide = 0;
 
    // Запускаем таймер
 
    slideTimer = setTimeout(function(){
        changeSlides('next');
    },config.slideDur);

Так как вы видите, после таймаута равного настройке slideDur, мы собираемся вызвать новую функцию, названную changeSlides(). Мы передаем ей единственный аргумент со значением next, означающим, что мы переходим к новому слайду в DOM. Наш аргумент changeSlides должен принимать три разных значения: next, prev а также порядковый номер слайда на случай, если мы захотим перейти к определенному слайду (вне порядка). Давайте перейдем к написанию этой функции.

Смена слайдов

function changeSlides(target){
 
    // Создаем условный фреймворк для обработки 
    // трех возможных вариантов
 
    if(target == 'next'){
 
        // Нацеливаемся на следующий слайд
 
        newSlide = activeSlide + 1;
 
        // Проверяем не дошли ли мы до конца набора, и если да,
        // то возвращаемся на начало цикла
 
        if(newSlide > totalSlides - 1){ 
            newSlide = 0;
        }
 
    } else if(target == 'prev'){
 
        // Нацеливаемся на предыдущий слайд
 
        newSlide = activeSlide - 1;
 
        // Проверяем не дошли ли мы до начало набора, и если да,
        // то возвращаемся в конец цикла
 
        if(newSlide < 0){ 
            newSlide = totalSlides - 1;
        }
 
    } else {
 
        // Если не следующий и не предыдущий, нацеливаемся на определенное число,
        // таким образом целью является переменная newSlide
 
        newSlide = target;
 
    };
 
    animateSlides(activeSlide, newSlide);
 
};

Теперь мы вызвали третью функцию под названием animateSlides(), которую мы будем использовать для того, чтобы управлять анимацией обесцвечивания. Функция animateSlides будет принимать два аргумента – порядковый номер текущего активного слайда, а также порядковый номер нового будущего слайда. Эти номера относятся к позиции слайда в DOM относительно своих собратьев и не должны быть приняты за z-index, который определяет вертикальный порядок следования.

Запомните: Мне нравится оборачивать код в его собственную функцию тогда, когда мне кажется, что его можно отделить как модуль, который можно использовать множество раз или вызывать во многих местах. Разбивание кода на модульные функции сделает его легче для чтения и поддержки. Так как эти второстепенные функции вложены в нашу родительскую функцию easyFader(), они будут иметь доступ к нашим настройкам конфигурации, а также приватным переменным.

Анимация обесцвечивания

function animateSlides(activeNdx, newNdx){
 
    // Задаем z-index 3 активному слайду
 
    $slides.eq(activeNdx).css('z-index', 3);
 
    // Предыдущему слайду задаем z-index 2 а 
    // opacity равное 1
 
    $slides.eq(newNdx).css({
        'z-index': 2,
        'opacity': 1
    });
 
    // Давайте будет придерживаться метода jQuery .animate() пока
    // и обесцвечиваем текущий слайд, чтобы показать новый слайд под ним
 
    $slides.eq(activeNdx).animate({'opacity': 0}, config.fadeDur,
    function(){
 
        // Удаляем у элемента все встроенные стили
 
        $slides.eq(active).removeAttr('style');
 
        // Новый слайд становится активным
 
        activeSlide = newNdx;
 
        // Запускаем таймер
 
        waitForNext();
 
        });
 
    };

Обратите внимание на то, что анимация определяет свою длительность согласно настройке конфигурации fadeDur. Последний кусочек головоломки – это функция waitForNext(). Если вы помните, мы объявили приватную переменную” slideTimer” в самом начале. Теперь этой переменной мы зададим функцию setTimeout() для того, чтобы она автоматически вызывала следующий слайд по истечению 7 секунд.

Таймер

function waitForNext(){
 
    slideTimer = setTimeout(function(){
        changeSlides('next');   
    },config.slideDur);
};

Поздравляю, если вы дочитали до сих пор – мы только что создали прототип нашего первого плагина! Вот весь код без комментариев с несколькими незначительными оптимизациями касательно порядка объявления переменных.

Полное собрание кода

function easyFader($container, slideDur, fadeDur){
    $container[0].faderConfig = {};
    var slideSelector = '.slide',
        slideTimer,
        activeSlide,
        newSlide,
        $slides = $container.find(slideSelector),
        totalSlides = $slides.length,
        config = $container[0].faderConfig;
    config = {
        slideDur : slideDur,
        fadeDur : fadeDur   
    };  
    $slides.eq(0).css('opacity', 1);
    activeSlide = 0;
    slideTimer = setTimeout(function(){
        changeSlides('next');
    },config.slideDur);
    function changeSlides(target){
        if(target == 'next'){
            newSlide = activeSlide + 1;
            if(newSlide > totalSlides - 1){ 
                newSlide = 0;
            }
        } else if(target == 'prev'){
            newSlide = activeSlide - 1;
            if(newSlide < 0){ 
                newSlide = totalSlides - 1;
            };
        } else {
            newSlide = target;
        };
        animateSlides(activeSlide, newSlide);
    };
    function animateSlides(activeNdx, newNdx){
        $slides.eq(activeNdx).css('z-index', 3);
        $slides.eq(newNdx).css({
            'z-index': 2,
            'opacity': 1
        });
        $slides.eq(activeNdx).animate({'opacity': 0}, config.fadeDur, function(){
            $slides.eq(activeNdx).removeAttr('style');
            activeSlide = newNdx;
            waitForNext();
        });
    };
    function waitForNext(){
        slideTimer = setTimeout(function(){
            changeSlides('next');   
        },config.slideDur);
    };
};

Достаточно легкий, не правда ли? Если вы удивляетесь почему он настолько мал в сравнении с большинством плагинов для слайд шоу, которые весят 30+Kb, то значит мы движемся в верном направлении.

Посмотреть рабочий прототип на Codepen

Фаза 3: Оптимизация, оттачивание и расширение

Первый шаг в оптимизации – это определить какие участки код повторяются без надобности. Так как наш плагин слишком мал на данном этапе, их будет немного, но один я уже вижу. Функция setTimeout(), которая вызывается при инициализации содержит тот же код, что и функция waitForNext(). Так что давайте будет вызывать эту функцию при инициализации. Кроме того, все выглядит достаточно хорошо на данный момент, и мы минимизируем код в дальнейшем.

Давайте перейдем к улучшениям. Настоящие улучшения и развитие происходят обычно по истечению недель и месяцев, когда мы попробуем плагин в разных средах, идентифицируем баги, добавим функционал и перепишем участки кода. Однако, для целей этой статьи, улучшение будет состоять в добавлении некоторого функционала, которого мы не включили в первоначальный прототип. Это будет:

Кнопки вперед/назад и управление пагинацией

Замена метода jQuery .animate() на переходы CSS3, где возможно

Кнопки и элементы управления

Мы можем либо автоматически генерировать кнопки и элементы управления с помощью jQuery, или же включить их в верстку, когда мы пишем HTML. Я куда больше предпочитаю второй вариант, так как я считаю, что решения относительно разметки, такие как стилизации, должны быть на усмотрение пользователя, а не продиктованы плагином.

решения относительно разметки, такие как стилизации, должны быть на усмотрение пользователя, а не продиктованы плагином.

Однако, что касается списка кнопок пагинации, предпочтительнее сгенерировать их динамически на основе количества слайдов, что является очень полезным, для ситуаций, связанных с CMS.

Для кнопок управления, достаточно будет чего-то такого:

<div id="Fader" class="fader">
    <img class="slide" src="..."/>
    <img class="slide" src="..."/>
    ...
    <div class="fader_controls">
        <div class="page" data-target="prev" ></div>
        <div class="page" data-target="next" ></div>
        <ul class="pager_list"></ul>
    </div>
</div>

Мы просто привяжем обработчики клика к чему угодно внутри контейнера, что имеет класс page. Я также создал пустой элемент ul с классом pager_list, который будет содержать кнопки пагинации. Класс fader_controls там находится только для стилизации. Снова пришло время JavaScript.

В первую очередь, давайте построим список кнопок. Это произойдет при инициализации и будет выглядеть как-то так:

var $pagerList = $container.find('.pager_list');
 
for(var i = 0; i < totalSlides; i++){
    $pagerList
        .append('<li class="page" data-target="'+i+'">'+i+'</li>'); 
};

Мы также зададим первого элементу li в списке класс active при инициализации, чтобы мы стилизовать его таким образом, чтобы пользователь видел, что первый слайд активен.

$pagers.eq(0).addClass('active');

Теперь я собираюсь создать наши обработчики событий. Этот кусок кода привяжет обработчики событий к нашим кнопками и элементам управления, и также будет вызываться при инициализации:

$container.find('.page').bind('click',function(){
    var target = $(this).attr('data-target');
    changeSlides(target);
});

Таким образом, всегда когда нажатие мыши происходит на любом элементе с классом page, вызывается функция changeSlides() с аргументом, который является значение атрибута data-target текущего элемента. Снова три возможных значения – next, prev или число.

Однако, есть одна небольшая проблема с этой функцией, состоящая в том, что таймер слайда, будет продолжать работать в фоновом режиме во время того как нажаты кнопки. Это может привести к тому, что слайд шоу будет внезапно сменять слайды, когда мы этого не ожидаем или не хотим. Чтобы избежать этого, нам нужно очистить этот таймер, когда нажимается кнопка. Вот переработанная функция:

$container.find('.page').bind('click',function(){
    var target = $(this).attr('data-target');
    clearTimeout(slideTimer);
    changeSlides(target);
});

Аналогично, мы хотим убедиться в том, что если у нас на данный момент сменяются слайд, то пользователь не должен иметь возможность нажимать на кнопки и прервать анимацию, вызывая множественные анимации одновременно. Чтобы добиться этого, мы создадим новую приватную переменную с именем fading, которая будет иметь значение false по умолчанию. Когда анимация начинается, эта переменная будет принимать значение true и обратно false, когда она заканчивается. Если поступает вызов функции animateSlides() во время анимации, мы его заблокируем, возвращая значение false, вот так:

function animateSlides(activeNdx, newNdx){
    if(fading){
        return false;
    };
    fading = true;
    ...

Это также будет хорошим временем для блокировки любого вызова, где пользователь переключает уже активный слайд:

function animateSlides(activeNdx, newNdx){
    if(fading || activeNdx == newNdx){
        return false;
    };
    fading = true;
    ...

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

$pagers.removeClass('active').eq(newSlide).addClass('active');

Переходы CSS3

Переходы CSS3 работают очень похожим способом, как и метод jQuery .animate() – плавно анимируют любое числовое свойство CSS3 от одного значения к другому. Большая разница состоит в том, чтобы отображение переходов CSS3 производится браузером без помощи JavaScript, и часто получит ускорение GPU, таким образом значительно уменьшая нагрузку на процессор и увеличивая частоту кадров. Это особенно полезно для портативных устройств, таких как планшеты и телефоны с очень плохой поддержкой JavaScript. Теперь, когда практически все браузеры поддерживают переходы CSS3, имеет смысл использовать их по умолчанию как способ анимации, используя jQuery .animate() тогда, когда нам нужно поддерживать IE9 (потому что IE8 даже не поддерживает opacity, поэтому в этой статье мы о нем забудем).

Для нашего плагина, мы в первую очередь убедимся, что переходы CSS3 доступны, а затем условно выберем наш метод анимации внутри функции animateSlides().

Вот отличный код я использовал для MixItUp, чтобы протестировать поддержку переходов CSS3 и если необходимо добавить префиксы производителей. Мы могли бы использовать библиотеку для определения поддержки браузером как Modernizr, но цель этого упражнения в том, чтобы создать экстра легкий самостоятельный плагин, который не будет иметь зависимостей кроме самой jQuery.

function prefix(el) {
    var prefixes = ["Webkit", "Moz", "O", "ms"];
    for (var i = 0; i < prefixes.length; i++){
        if (prefixes + "Transition" in el.style){
                return '-'+prefixes.toLowerCase()+'-';
        };
    };
    return "transition" in el.style ? "" : false;
};

Мы просто передадим любой сырой элемент DOM ему, и он или возвратит необходимый префикс для браузера или значение false, если поддержки нет. Если поддержка не требует префикса (что вскоре станет нормой), она возвратит пустую строку. Мы вызовем ее следующим образом:

var prefix = prefix($container[0]);

Если переменная prefix имеет значение false, значит, поддержки нет. Иначе мы дополняем строку, чтобы она добавлял необходимый код CSS.

Теперь, когда обнаружение переходов CSS3 установлено, снова обратимся к нашей функции animateSlides().

function animateSlides(activeNdx, newNdx){
    if(fading || activeNdx == newNdx){
        return false;
    };
    fading = true;
    $pagers.removeClass('active').eq(newSlide).addClass('active');
    $slides.eq(activeNdx).css('z-index', 3);
    $slides.eq(newNdx).css({
        'z-index': 2,
        'opacity': 1
    });
    if(!prefix){ // Если переходы не поддерживаются, использовать jQuery
        $slides.eq(activeNdx).animate({'opacity': 0}, config.fadeDur, 
            function(){
                $slides.eq(active).removeAttr('style');
                activeSlide = newNdx;
                fading = false;
                waitForNext();
        });
    } else { // Иначе использовать CSS3
 
        // Так как мы не можем использовать как названия собственные,
        // нам нужно создать объект, который будет содержать наши стили,
        // и затем применить наши свойства к ним посредством следующего синтаксиса
 
        var styles = {};
        styles[prefix+'transition'] = 'opacity '+config.fadeDur+'ms';
        styles['opacity'] = 0;
 
        // теперь получим данные из объекта при помощи метода .css()
        $slides.eq(activeNdx).css(styles);
 
        // теперь подождем когда анимация закончится
        var fadeTimer = setTimeout(function(){
            $slides.eq(activeNdx).removeAttr('style');
            activeSlide = newNdx;
            fading = false;
            waitForNext();
        },config.fadeDur);
    };
};

Мы также могли бы просто объявить переход непрозрачности множество раз со всеми необходимыми префиксами используя метод .css(), но этот метод значительно чище, так как он направлен только на тот браузер, который используется. Вы могли заметить, что есть пространство для оптимизации здесь, когда код исполняется после того, как анимация закончится, повторяется для обоих методов. Давайте вынесем его в отдельную функцию и назовем ее cleanup(), которая будет находиться внутри animateSlides():

function cleanUp(){
    $slides.eq(activeNdx).removeAttr('style');
    activeSlide = newNdx;
    fading = false;
    waitForNext();
};

Вот и все! Вот версия № 2:

Полная агрегированная версия № 2 (или Премиум)

function easyFader($container, slideDur, fadeDur){
    $container[0].faderConfig = {};
    var slideSelector = '.slide',
        fading = false,
        slideTimer,
        activeSlide,
        newSlide,
        $slides = $container.find(slideSelector),
        totalSlides = $slides.length,
        $pagerList = $container.find('.pager_list');
        prefix = prefix($container[0]),
        config = $container[0].faderConfig;
    config = {
        slideDur : slideDur,
        fadeDur : fadeDur   
    };
    for(var i = 0; i < totalSlides; i++){
            $pagerList
                .append('<li class="page" data-target="'+i+'">'+i+'</li>');
    };
    $container.find('.page').bind('click',function(){
        var target = $(this).attr('data-target');
        clearTimeout(slideTimer);
        changeSlides(target);
    });
    var $pagers = $pagerList.find('.page');
    $slides.eq(0).css('opacity', 1);
    $pagers.eq(0).addClass('active');
    activeSlide = 0;
    slideTimer = setTimeout(function(){
        changeSlides('next');
    },config.slideDur);
    function changeSlides(target){
        if(target == 'next'){
            newSlide = activeSlide + 1;
            if(newSlide > totalSlides - 1){
                newSlide = 0;
            }
        } else if(target == 'prev'){
            newSlide = activeSlide - 1;
            if(newSlide < 0){
                newSlide = totalSlides - 1;
            };
        } else {
            newSlide = target;
        };
        animateSlides(activeSlide, newSlide);
    };
    function animateSlides(active, new){
        if(fading || activeNdx == newNdx){
            return false;
        };
        fading = true;
        $pagers.removeClass('active').eq(newSlide).addClass('active');
        $slides.eq(active).css('z-index', 3);
        $slides.eq(new).css({
            'z-index': 2,
            'opacity': 1
        });
        if(!prefix){
            $slides.eq(active).animate({'opacity': 0}, config.fadeDur,
            function(){
                cleanUp();
            });
        } else {
            var styles = {};
            styles[prefix+'transition'] = 'opacity '+config.fadeDur+'ms';
            styles['opacity'] = 0;
            $slides.eq(active).css(styles);
            var fadeTimer = setTimeout(function(){
                cleanUp();
            },config.fadeDur);
        };
        function cleanUp(){
            $slides.eq(active).removeAttr('style');
            activeSlide = new;
            fading = false;
            waitForNext();
        };
    };
    function waitForNext(){
        slideTimer = setTimeout(function(){
            changeSlides('next');
        },config.slideDur);
    };
    function prefix(el){
        var prefixes = ["Webkit", "Moz", "O", "ms"];
        for (var i = 0; i < prefixes.length; i++){
            if (prefixes + "Transition" in el.style){
                return '-'+prefixes.toLowerCase()+'-'; 
            };
        }; 
        return "transition" in el.style ? "" : false;
    };
};

Фаза 4: Превращение в настоящий jQuery плагин

Последним шагом будет превращение этого:

easyFader($('#Fader'),7000,800);

В это:

$('#Fader').easyFader({
    slideDur: 7000,
    fadeDur: 800
});

Делая это, мы превращаем функцию в метод jQuery, который мы можем вызвать на нашем объекте jQuery $(‘#Fader’). Чтобы добиться этого, нам нужно инкапсулировать весь наш код во что-то такое:

(function($){
 
    var methods = {
 
        //Объявим метод init
 
        init: function(settings){
 
            // Всегда возвращайте "this" для того, чтобы jQuery методы могли следовать один за другим
 
            return this.each(function(){
 
                // Создаем конфигурационный объект 
                // с предопределёнными значениями по умолчанию
 
                var config = {
                    slideDur: 7000,
                    fadeDur: 800
                };
 
                // Если пользователь определяет конфигурационные настройки,
                // соединяем их в конфигурационный объект,
                // чтобы перезаписать значения по умолчанию
 
                if(settings){
                    $.extend(config, settings);
                };
 
                // Весь остальной код будет здесь
 
                };
            });
        }
    };
 
    // Объявляем плагин
 
    $.fn.easyFader = function(settings){
 
        // Вызываем метод init при инициализации
 
        return methods.init.apply(this, arguments);
    };
 
})(jQuery);

С помощью этого кода мы создаем приватную функцию с зависимостью JQuery, и в ней мы объявляем имя плагина jQuery «easyFader», и его доступные методы. Сейчас нам нужно только инициализации метод «init», но эта структура позволит нам добавить дополнительные методы позже, если мы захотим.

Установив по умолчанию значения для slideDur и fadeDur (7000 мс и 800 мс), мы гарантируем, что пользователь может запустить плагин прямо из коробки, без необходимости указывать эти два значения. Если пользователь решит указать их определенно, значения по умолчанию будут перезаписаны теми значениями, которые определит пользователь.

Запомните: в идеальном варианте, мы могли бы конвертировать большинство наших функций (например, changeSlides () и animateSlides ()) в свои методы. В этой статье, однако, мы оставим их в виде функций, чтобы избежать необходимости иметь дело с более передовыми концепциями, как конструктора функций и прототипов.

Чтобы дать возможность любому JavaScript коду исполняться как можно быстрее, мы захотим определить наши функции, прежде чем мы их вызываем, что является противоположностью того, что мы делали до сих пор. Таким образом, вы заметите, новый обратный порядок выполнения в окончательном коде.

Не мудрствуя лукаво, я представляю jQuery EasyFader!

(function($){
    function prefix(el){
        var prefixes = ["Webkit", "Moz", "O", "ms"];
        for (var i = 0; i < prefixes.length; i++){
            if (prefixes + "Transition" in el.style){
                return '-'+prefixes.toLowerCase()+'-'; 
            };
        }; 
        return "transition" in el.style ? "" : false;
    };
    var methods = {
        init: function(settings){
            return this.each(function(){
                var config = {
                    slideDur: 7000,
                    fadeDur: 800
                };
                if(settings){
                    $.extend(config, settings);
                };
                this.config = config;
                var $container = $(this),
                    slideSelector = '.slide',
                    fading = false,
                    slideTimer,
                    activeSlide,
                    newSlide,
                    $slides = $container.find(slideSelector),
                    totalSlides = $slides.length,
                    $pagerList = $container.find('.pager_list');
                    prefix = prefix($container[0]);
                function waitForNext(){
                    slideTimer = setTimeout(function(){
                        changeSlides('next');
                    },config.slideDur);
                };
                function animateSlides(activeNdx, newNdx){
                    function cleanUp(){
                        $slides.eq(activeNdx).removeAttr('style');
                        activeSlide = newNdx;
                        fading = false;
                        waitForNext();
                    };
                    if(fading || activeNdx == newNdx){
                        return false;
                    };
                    fading = true;
                    $pagers.removeClass('active').eq(newSlide).addClass('active');
                    $slides.eq(activeNdx).css('z-index', 3);
                    $slides.eq(newNdx).css({
                        'z-index': 2,
                        'opacity': 1
                    });
                    if(!prefix){
                        $slides.eq(activeNdx).animate({'opacity': 0}, config.fadeDur,
                        function(){
                            cleanUp();
                        });
                    } else {
                        var styles = {};
                        styles[prefix+'transition'] = 'opacity '+config.fadeDur+'ms';
                        styles['opacity'] = 0;
                        $slides.eq(activeNdx).css(styles);
                        var fadeTimer = setTimeout(function(){
                            cleanUp();
                        },config.fadeDur);
                    };
                };
                function changeSlides(target){
                    if(target == 'next'){
                        newSlide = activeSlide + 1;
                        if(newSlide > totalSlides - 1){
                            newSlide = 0;
                        }
                    } else if(target == 'prev'){
                        newSlide = activeSlide - 1;
                        if(newSlide < 0){
                            newSlide = totalSlides - 1;
                        };
                    } else {
                        newSlide = target;
                    };
                    animateSlides(activeSlide, newSlide);
                };
                for(var i = 0; i < totalSlides; i++){
                    $pagerList
                        .append('<li class="page" data-target="'+i+'">'+i+'</li>');
                };
                $container.find('.page').bind('click',function(){
                    var target = $(this).attr('data-target');
                    clearTimeout(slideTimer);
                    changeSlides(target);
                });
                var $pagers = $pagerList.find('.page');
                $slides.eq(0).css('opacity', 1);
                activeSlide = 0;
                slideTimer = setTimeout(function(){
                    changeSlides('next');
                },config.slideDur);
            });
        }
    };
    $.fn.easyFader = function(settings){
        return methods.init.apply(this, arguments);
    };
})(jQuery);

Как и все подобные вещи, нет абсолютного «правильно» и «неправильно». Может быть, вы заметили, что вы сделали бы по-другому или могли бы улучшить — и в этом вся прелесть и стимул – в желании написать свои собственные инструменты. Я надеюсь, что вы нашли эту информацию полезной.

Это, конечно, не конец для EasyFader, так как я намерен добавить кучу дополнительных функций, таких как функции обратного вызова и еще много настраиваемых параметров, в надежде, что он может стать новым слайд-шоу плагином «номер один» для всех моих проектов. Но на сегодняшний день, не забудьте проверить и скопировать готовую версию на GitHub. Каких-то 1.2KB в сжатом виде!

Автор: Scratch

Источник: http://www.barrelny.com/blog/

Редакция: Команда webformyself.

Хотите научиться делать фотогалереи как Вконтакте?

Прямо сейчас изучите курс по созданию красивой фотогалареи как Вконтакте!

Смотреть курс

Метки: , ,

Комментарии Вконтакте:

Комментарии Facebook:

Комментарии (4)

  1. Александр

    Здравствуйте Андрей! Скажите пожалуйста, в бонус уроках к курсу JQ с нуля до профи Вы рассказываете, как создать плагин tooltip, каким редактором кода Вы пользуетесь? Это WebStorm?
    И каким образом Вы так быстро прописываете теги, например, написав имя тега и его класс, после чего видно, что нажимаете какие-то кнопки и оставшаяся часть кода сама прописывается?
    И последний вопрос, как Вы делаете так, что в вашем редакторе Вы редактируете css код, и сразу параллельно в браузере наблюдаете, как будет выглядеть элемент (то когда Вы создаёте всплывающую подсказку для плагина). Помнится, что в каком то видеокурсе Андрей Бернацкий рассказывал об этой фишке, но не помню в каком, а копаться в уроках, как то не хочется. Как сделать, чтобы данная фишка работала?

    • Андрей Кудлай

      Здравствуйте, Александр.
      Редактор называется Sublime Text. К нему (и не только к нему) есть замечательный плагин Emmet (вот урок по нему — Плагин Emmet — пишем код быстрее), который и позволяет ускорить написание кода. Ну и есть различные дополнения для браузеров (Auto Reload, Live Reload), позволяющие браузеру обновлять страницу после изменения ее кода либо по таймауту.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree