Кнопки со встроенным индикатором выполнения

Просмотр

Скачать

Индикаторы выполнения (англ. Progress bars) стали очень популярны в последнее время, и есть большое количество плагинов, которые помогут вам добавить индикатор выполнения на ваш сайт. Но как сделать такой индикатор самим? Проблема в том, что уже есть множество реализаций, поэтому в этом уроке мы собираемся запрограммировать кое-что другое – кнопки, в которые будут встроены индикаторы выполнения.

Они отлично подходят для отображения прогресса, пока отправляются формы или загружается контент с помощью AJAX. Кроме того, эти кнопки будут использовать стили и трансформации CSS, что сделает их легко настраиваемыми под нужды пользователя.

Содержание

HTML

В первой части урока мы напишем разметку HTML. Она представляет собой стандартный HTML документ, включающий в себя два дополнительных источника, которые мы обсудим позже – таблицу стилей styles.css и файл JavaScript script.js. Дополнительно я подключил библиотеку jQuery и шрифт Raleway из Google web fonts.

index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Tutorial: Buttons with built-in progress meters</title>
        <link href="http://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet" />
        <!-- The Stylesheets -->
        <link href="assets/css/style.css" rel="stylesheet" />
    </head>
    <body>
        <h1>Progress Buttons</h1>
        <a id="submitButton" href="#" class="progress-button">Submit</a>
        <a id="actionButton" href="#" class="progress-button green" data-loading="Working.." data-finished="Finished!" data-type="background-bar">Action!</a>
        <a id="generateButton" href="#" class="progress-button red" data-loading="Generating.." data-finished="Download" data-type="background-vertical">Generate</a>

        <h1>Progress Control</h1>
        <a id="controlButton" href="#" class="progress-button">Start</a>
        <div class="control-area">
            <a class="command increment">Increment</a>
            <a class="command set-to-1">Set to 1%</a>
            <a class="command set-to-50">Set to 50%</a>
            <a class="command finish">Finish</a>
        </div>
        <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script src="assets/js/script.js"></script>
    </body>
</html>

Эта разметка очень простая. Кнопки с индикатором выполнения определены как обычные гиперссылки. Чтобы плагин смог распознать их и превратить в кнопки со встроенным индикатором процесса, к ним должен быть применен класс .progress-button.

Кнопки могут быть также сконфигурированы установкой трех data- атрибутов:

  • data-type определяет какой тип индикатора выполнения будет показан. В настоящее время поддерживаются три типа: background-horizontal (горизонтальный, установлен по умолчанию), background-bar (в виде полосы) и background-vertical (вертикальный);
  • data-loading определяет какой текст будет показан, пока индикатор выполнения продвигается. Значение по умолчанию – «Loading..»;
  • data-finished отвечает за текст, который появится на кнопке, когда процесс завершится. Значение по умолчанию – «Done!».
  • Если вы не определите какой-нибудь атрибут, то для него будет использовано значение по умолчанию.

HTML


Кнопки со встроенным индикатором выполнения

Код jQuery

В этой части урока мы напишем JavaScript и jQuery код для того, чтобы наши кнопки заработали. Код организован в виде шести плагинов jQuery, начинающихся одинаково: progressInitialize, progressStart, progressIncrement, progressTimed, progressSet и progressFinish. Я достаточно подробно прокомментировал код, так что вы легко сможете разобраться в нем:

assets/js/script.js
$(document).ready(function(){
    // Конвертируем все ссылки с классом progress-button
    // в настоящие кнопки с индикатором выполнения.
    // Вам нужно вызвать эту функцию, как только загрузится страница
    //Если вы добавите кнопки позже, вам нужно будет вызвать эту функцию только для них.

    $('.progress-button').progressInitialize();

    //Ожидаем клика по одной из первых трех кнопок и начинаем анимацию прогресса. 

    $('#submitButton').click(function(e){
        e.preventDefault();

// Эта функция показывает индикатор выполнения 
// в течение определенного промежутка времени. 

        $(this).progressTimed(2);
    });

    $('#actionButton').click(function(e){
        e.preventDefault();
        $(this).progressTimed(2);
    });

    $('#generateButton').one('click', function(e){
        e.preventDefault();

        // Можно использовать функцию обратного вызова (callback).

        var button = $(this);
        button.progressTimed(3, function(){

            //В этой функции обратного вызова вы можете установить для атрибута кнопки href  
            // значение URL сгенерированного файла. Здесь мы только 
            // устанавливаем новый обработчик события, который генерирует предупреждение.

            button.click(function(){
                alert('Showing how a callback works!');
            });
        });
    });

    // Пользовательская обработка прогресса.

    var controlButton = $('#controlButton');

    controlButton.click(function(e){
        e.preventDefault();

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

        controlButton.progressStart();
    });

    $('.command.increment').click(function(){

        // Инкрементируем индикатор на 10%. Можно передать число как аргумент функции
        // чтобы увеличивать на другое  значение.

        controlButton.progressIncrement();
    });

    $('.command.set-to-1').click(function(){

        // Устанавливаем индикатор прогресса на определенный процент.

        controlButton.progressSet(1);
    });

    $('.command.set-to-50').click(function(){
        controlButton.progressSet(50);
    });

    $('.command.finish').click(function(){

        // Устанавливаем индикатор прогресса в 100 % и выводим текст о готовности.
        controlButton.progressFinish();
    });

});

// Функционал индикатора выполнения доступен в виде ряда плагинов.
// Вы можете поместить этот код в отдельный файл, если хотите соблюдать порядок.

(function($){

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

    $.fn.progressInitialize = function(){

        // Эта функция создает необходимую разметку для индикатора выполнения
        //и определяет несколько «слушателей» событий.

        // Цикл по всем кнопкам:

        return this.each(function(){

            var button = $(this),
                progress = 0;

            // Извлечение атрибутов данных в объект option.
            // Если атрибуты не указаны, к ним будут применены значения по умолчанию.

            var options = $.extend({
                type:'background-horizontal',
                loading: 'Loading..',
                finished: 'Done!'
            }, button.data());

            // Добавляем атрибуты data-, если они отсутствуют у элемента. 
            // Они применяются в нашем CSS коде, чтобы выводить сообщения.
            button.attr({'data-loading': options.loading, 'data-finished': options.finished});

            // Добавляем необходимую разметку к кнопке для индикатора выполнения.
            var bar = $('<span class="tz-bar ' + options.type + '">').appendTo(button);

            // Событие progress заставляет кнопку обновить индикатор выполнения.
            button.on('progress', function(e, val, absolute, finish){

                if(!button.hasClass('in-progress')){

                    // Это первое событие выполнения для кнопки 
                    // (или первое после завершения предыдущего прогона). Повторно инициализируем
                    // выполнение и удаляем некоторые классы, которые могли остаться.

                    bar.show();
                    progress = 0;
                    button.removeClass('finished').addClass('in-progress')
                }

            // val, absolute и finish – это сведения о событии, отправляемые функциями
            // progressIncrement и progressSet, которые вы можете увидеть ближе к концу этого файла.

                if(absolute){
                    progress = val;
                }
                else{
                    progress += val;
                }

                if(progress >= 100){
                    progress = 100;
                }

                if(finish){

                    button.removeClass('in-progress').addClass('finished');

                    bar.delay(500).fadeOut(function(){

                        //Запускаем пользовательское событие progress-finish.
                        button.trigger('progress-finish');
                        setProgress(0);
                    });

                }

                setProgress(progress);
            });

            function setProgress(percentage){
                bar.filter('.background-horizontal,.background-bar').width(percentage+'%');
                bar.filter('.background-vertical').height(percentage+'%');
            }

        });

    };

    // progressStart симулирует активность на индикаторе выполнения. Вызываем ее вначале,  
    // если выполнение займет долгое время.

    $.fn.progressStart = function(){

        var button = this.first(),
            last_progress = new Date().getTime();

        if(button.hasClass('in-progress')){
            //Не запускаем во второй раз!
            return this;
        }

        button.on('progress', function(){
            last_progress = new Date().getTime();
        });

        // Каждые полсекунды проверяем, был ли прогресс увеличен за последние 2 секунды.

        var interval = window.setInterval(function(){

            if( new Date().getTime() > 2000+last_progress){

                // Здесь не было активности за последние 2 секунды. 
                // Увеличиваем индикатор выполнения немного, чтобы показать, что что-то происходит. 

                button.progressIncrement(5);
            }

        }, 500);

        button.on('progress-finish',function(){
            window.clearInterval(interval);
        });

        return button.progressIncrement(10);
    };

    $.fn.progressFinish = function(){
        return this.first().progressSet(100);
    };

    $.fn.progressIncrement = function(val){

        val = val || 10;

        var button = this.first();

        button.trigger('progress',[val])

        return this;
    };

    $.fn.progressSet = function(val){
        val = val || 10;

        var finish = false;
        if(val >= 100){
            finish = true;
        }

        return this.first().trigger('progress',[val, true, finish]);
    };

    // Эта функция создает индикатор выполнения, который 
    // завершается через определенный промежуток времени.

    $.fn.progressTimed = function(seconds, cb){

        var button = this.first(),
            bar = button.find('.tz-bar');

        if(button.is('.in-progress')){
            return this;
        }

        // Определяем переход transition в течение всей работы индикатора.  
        // CSS сделает работу по анимации индикатора выполнения за нас. 
        bar.css('transition', seconds+'s linear');
        button.progressSet(99);

        window.setTimeout(function(){
            bar.css('transition','');
            button.progressFinish();

            if($.isFunction(cb)){
                cb();
            }

        }, seconds*1000);
    };

})(jQuery);

РrogressInitialize устанавливает «слушателя» для пользовательского события прогресса, которое вызывается другими функциями, когда необходимо обновить индикатор. Благодаря пользовательскому событию у нас есть возможность использовать полностью независимые функции, такие как progressStart, которая управляет своим временем и состоянием – при этом функция progresInitialize может и не знать о progressStart.

Другая важная деталь заключается в установке двух специальных классов для кнопок: .in-progress во время передвижения индикатора выполнения, и .finished, когда выполнение завершено. Они используются для обновления текста кнопок, как вы увидите в следующей секции.

CSS

Выше я упоминал, что мы определяем два CSS класса на кнопках - .in-progress и .finished. Но как добавление одного из этих классов изменяет текст кнопки? Да очень просто – мы используем хитрый ход в CSS, включающий в себя использование CSS3 оператора attr, который в комбинации с контентом может устанавливать текст :before или :after псевдоэлемента к атрибуту элемента. Вам все станет ясно, если вы посмотрите код сами (строки 44-52):

assets/css/styles.css
.progress-button{
    display: inline-block;
    font-size:24px;
    color:#fff !important;
    text-decoration: none !important;
    padding:14px 60px;
    line-height:1;
    overflow: hidden;
    position:relative;

    box-shadow:0 1px 1px #ccc;
    border-radius:2px;

    background-color: #51b7e6;
    background-image:-webkit-linear-gradient(top, #51b7e6, #4dafdd);
    background-image:-moz-linear-gradient(top, #51b7e6, #4dafdd);
    background-image:linear-gradient(top, #51b7e6, #4dafdd);
}

/* Скрываем первоначальный текст кнопки. После этого текст loading или finished
будет показан в элементе :after поверх кнопки. */

.progress-button.in-progress,
.progress-button.finished{
    color:transparent !important;
}

.progress-button.in-progress:after,
.progress-button.finished:after{
    position: absolute;
    z-index: 2;
    width: 100%;
    height: 100%;
    text-align: center;
    top: 0;
    padding-top: inherit;
    color: #fff !important;
    left: 0;
}

/* Если класс .in-progress class применен к кнопке, отображаем содержимое атрибута data-loading на кнопку */

.progress-button.in-progress:after{
    content:attr(data-loading);
}

/* То же самое для класса  .finished */

.progress-button.finished:after{
    content:attr(data-finished);
}

/* Яркая полоска, растущая в зависимости от прогресса */

.progress-button .tz-bar{
    background-color:#e667c0;
    height:3px;
    bottom:0;
    left:0;
    width:0;
    position:absolute;
    z-index:1;

    border-radius:0 0 2px 2px;

    -webkit-transition: width 0.5s, height 0.5s;
    -moz-transition: width 0.5s, height 0.5s;
    transition: width 0.5s, height 0.5s;
}

/* Полоска может быть, как горизонтальная, так и вертикальная */

.progress-button .tz-bar.background-horizontal{
    height:100%;
    border-radius:2px;
}

.progress-button .tz-bar.background-vertical{
    height:0;
    top:0;
    width:100%;
    border-radius:2px;
}

Остальной код настраивает кнопки и встроенный индикатор выполнения. В файле styles.css я также подключил две дополнительные цветовые темы и некоторые другие правила, которые не приведены здесь, но вы можете посмотреть их сами в исходном коде.

Готово!

Код, который мы написали сегодня, готов для настройки. Просто откройте файл styles.css в вашем любимом редакторе кода и поменяйте цвета, шрифты или стили так, чтобы они подходили к вашему сайту. Измените текст, отредактировав HTML и атрибуты данных. Или вы даже можете улучшить код с помощью каких-нибудь крутых новинок. В таком случае не забудьте поделиться им в комментариях.

РедакцияПеревод статьи «Buttons With Built-in Progress Meters»