Javascript бегущий огонек

Javascript бегущий огонек

Понадобилось мне на днях сделать "бегущий огонек" на javascript, без jQuery и каких - либо зависимостей. После недолгого поиска в интернете решил сделать сам. Получился некий цикличный итератор над коллекцией DOM-элементов с таймаутом. Кому нужно - забирайте.

Так же было решено вынести всю специфику в клиентский код, потому как может кому то понадобится эффект не именно бегущий огонек, а допустим какой другой цикличный эффект над набором DOM-элементов, например подпрыгивание чего - либо или появление или исчезание, или любая другая логика, укладывающаяся в схему:

    while true:

        берем следующий элемент;
        если элементов больше нет - берем первый элемент;

        применяем к элементу первую ф-цию;
        ждем таймаут;

        применяем к элементу вторую ф-цию;
        ждем таймаут;

    endwhile;

В общем оставил чисто абстракцию, реализующую задачу описанную выше, но для примера "бегущий огонек" подойдет как раз кстати. В примере клиентские функции при вызове будут просто делать: одна - устанавливать класс lightOn, а вторая - убирать этот класс у текущего элемента. Так мы добьемся эффекта "бегущий огонек".

Бегущий огонек демо


 

Бегущий огонек код

"use strict";

/**
 * Цикличный итератор с временной задержкой.
 *
 * @param {NodeList|HTMLElement[]} oNodeList - массив или NodeList элементов
 * над которыми нужно циклично применять функции callbackStart и callbackEnd
 * @param {Function} callbackStart - функция обр. вызова, вызываемая при
 * переходе на элемент, получит текущий элемнт как параметр.
 * @param {Function} callbackEnd - функция обр. вызова, вызываемая при
 * уходе с элемента, получит текущий элемнт как параметр.
 * @param {number} speed - временная задержка в мсек.
 */
function TimeoutIterator(oNodeList, callbackStart, callbackEnd, speed) {
    var _index   = 0,
       _interval = 0;

    // Предохранитель от возможного вызова без "new"
    if (!(this instanceof TimeoutIterator.prototype.constructor)) {
        return new TimeoutIterator(oNodeList, callbackStart, callbackEnd, speed);
    }

    /**
     * Стартовая операция.
     *
     * Её предназначение вызвать или не вызвать 1-й коллбэк (callbackStart),
     * а затем спустя заданный интервал (speed) вызвать финишную операцию.
     *
     * @param {HTMLElement} element - текущий элемент набора
     */
    function startAction(element) {
        // если передан первый коллбэк - вызываем его:
        callbackStart && callbackStart(element);
        // с указанной задержкой...
        window.setTimeout(function () {
            // вызываем вторую операцию.
            stopAction(element);
        }, speed);
    }

    /**
     * Финишная операция.
     *
     * Её предназначение вызвать или не вызвать 2-й коллбэк (callbackEnd).
     *
     * @param {HTMLElement} element - текущий элемент набора
     */
    function stopAction(element) {
        callbackEnd && callbackEnd(element);
    }

    /**
     * Запустить.
     */
    this.run = function () {
        // Предохранитель от повторного запуска
        // уже работающего экземпляра.
        if (_interval > 0) {
            return;
        }
        _interval = window.setInterval(function () {
            // Для "удержания" индекса текущего элемента
            // в пределах размера oNodeList применяем
            // операцию деления по модулю.
            _index %= oNodeList.length;
            // Запускаем стартовую операцию:
            startAction(oNodeList[_index]);
            _index++;
        }, speed);
    };

    /**
     * Остановить.
     */
    this.stop = function () {
        // Просто очистим _interval
        window.clearInterval(_interval);
        _interval = 0;
    };
}

Использование "бегущий огонек"

Под "бегущий огонек" нам понадобится примерно следующая разметка:

<p>
    <button id="runner" type="button">СТАРТ</button>
    <button id="stop" type="button">СТОП</button>
</p>

<div id="iter-container">
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
    <div class="light"></div>
</div>

А сам скрипт вызываем следующим образом:

/** @type {NodeList} elementCollection - набор элементов для "бегущего огонька" */
var elementCollection = document.getElementById("iter-container")
        .getElementsByTagName("DIV"),

    lr = TimeoutIterator(
        elementCollection,

        /**
         * стартовая ф-ция: просто устанвливает имя класса lightOn
         * @param {HTMLElement} element - текущий элемент набора
         */
        function (element) {
            element.className += (element.className.length) ?
                " lightOn" : "lightOn";
        },

        /**
         * финишная ф-ция: просто убирает имя класса lightOn
         * @param {HTMLElement} element - текущий элемент набора
         */
        function (element) {
            element.className = element.className.replace(/\s?lightOn/, "");
        },

        /* задержка в мсек. */
        50
    );

/*  "Вешаем" обработчик для запуска действия */
document.getElementById("runner").onclick = function () {
    lr.run();
};

/*  "Вешаем" обработчик для остановки действия */
document.getElementById("stop").onclick = function () {
    lr.stop();
};

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


Защитный код
Обновить



Кто на сайте
Сейчас 90 гостей онлайн