Кнопки со встроенным прогресс баром

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

Но как же можно самостоятельно создать подобный эффект? Проблема заключается в том, что в интернете уже есть множество готовых примеров, и поэтому в нашем сегодняшнем руководстве мы хотим рассказать вам о чем-то немного другом – о разработке кнопок, в которые будут встроены индикаторы прогресса.  Такие кнопки будут идеальны для отображения прогресса отправки данных формы или выполнения AJAX запроса. Мы будем использовать стили CSS3, что позволит легко настроить кнопки под свои нужды.

HTML

Как всегда, начнем с HTML разметки.  Она представлена в виде стандартного HTML5-документа, который включает в себя два дополнительных ресурса, о которых мы с вами поговорим позже – styles.css и javascript-файл script.js. Вдобавок, мы также включили сюда jQuery-библиотеку и шрифт Raleway из библиотеки веб-шрифтов от Google.

<!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!

Если атрибут не указан — будет использовано значение по-умолчанию.

jQuery

Теперь напишем JavaScript код чтобы заставить кнопки работать.  Код будет организован в формат 6 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();

        // Можем передать колбэк

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

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

            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($){

    // Инициализация прогресс-бара

    $.fn.progressInitialize = function(){

        // Эта функция создает необходимую разметку
        // и отслеживает некоторые события.

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

        return this.each(function(){

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

            // Выделение data-* атрибутов в опции.
            // Если они не указаны - используются дефолтные значения

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

            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+'%');
            }

        });

    };

    $.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);

progressInitialize устанавливает слушателя событий для кастомных событий progress, которые запускают другие функции, когда индикатор следует обновить. Благодаря кастомному событию, у нас имеются полностью независимые функции вроде progressStart, которые имеют собственный таймер и положение — progresInitialize должен быть проинформирован о progressStart. Также важно отметить, что здесь мы задаем кнопкам 2 специальных класса — .in-progress, когда индикатор прогресса движется и .finished, когда движение будет завершено. Они обновляют текст на кнопках, как видно из следующего раздела.

CSS

Как уже было сказано мы используем два CSS класса для кнопок – .in-progress и .finished. Но как добавление одного из этих классов может изменить текст кнопки? Легко — мы используем CSS3 функцию attr , которая в сочетании с content, может изменить текст псевдо-элементов :before или :after. Станет понятнее, если посмотрите сами:

.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);
}

/* Прячем исходный текст кнопок. При загрузке текст будет показан
   в псевдо-элементе :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;
}

/* Если кнопка имеет класс, показываем
   содержимое атрибута 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 data-* атрибуты. Или даже создайте свои собственные новые фишки 🙂


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