Создание кнопок со встроенным индикатором процесса

13-10-30 JavaScript Animation, jQuery, Loading 0

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

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

HTML

Как всегда, начнем с HTML разметки.

Это будет обычный HTML5 с подключением пары файлов: стили — styles.css и скрипт JavaScript  — script.js. Так же подключим jQuery и Google шрифт Raleway.

<!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 плагинов: progressInitializeprogressStart, progressIncrement, progressTimedprogressSet и progressFinish.

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

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

У нас есть два специальных CSS класса: .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-* атрибуты. Или даже создайте свои собственные новые фичи 🙂

Хочешь получать статьи на почту?

Подпишись на обновления!
* Ваш email не будет разглашен/продан. Вы сможете отписаться в любое время.

Нет комментариев

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

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