Часы со стрелками без применения Flash

Для сайта компании «Международный Центр Финансовых Операций » заказчик попросил сделать часы со стрелками, показывающие время в Москве, Нью-Йорке и Токио. Первое, что могло бы прийти в голову – «Flash». А не тут-то было… Часики очень быстро можно сделать и на HTML+CSS+JavaScript.

Самое сложное и долгое это, пожалуй, спрайты стрелок.

Дизайнер приготовил для меня контуры стрелок в Photoshop, а я с помощь экшина создал 24 слоя, на которых стрелки повернуты на 15° относительно предыдущего слоя. Признаюсь, что эти значения были выбраны эмпирически исключительно исходя из здравого смысла. Часы на странице носят скорее декоративный характер, нежели практически и поэтому сомнительно, что кто-то будет за ними специально следить. Технически, можно было бы сделать и все 60 фаз.

Спрайты стрелок часов

Затем я расположил слои один над другим, чтобы изменять изображение исключительно с помощью смещения положения фона у блока.

В стрелки часов я отделил от фона с помощью техники, аналогичной той, что описывал Сергей Чикуенок в статье «Как вырезать картинку из фона ». Использовать PNG-24 с прозрачностью было не приемлемо из-за отсутствия поддержки в IE6, а отрисовка таких мелких деталей без антиалиасинга выглядела просто ужасно.

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


<div id="clocks">
    <div class="city moscow">
        <div class="hour"></div>
        <div class="minute"></div>
    </div>
    <div class="city newyork">
        <div class="hour"></div>
        <div class="minute"></div>
    </div>
    <div class="city tokyo">
        <div class="hour"></div>
        <div class="minute"></div>
    </div>
</div>

#clocks {
    background: url(clocks-template.png) no-repeat left top;
    width: 150px;
    height: 77px;
    position: relative;
}
#clocks .city,
#clocks .hour,
#clocks .minute {
    width: 45px;
    height: 45px;
    position: absolute;
}
#clocks .city {
    overflow: hidden;
    top: 14px;
}
#clocks .hour {
    background: url(clock-hour-sprite.gif) no-repeat 0 0;
}
#clocks .minute {
    background: url(clock-minute-sprite.gif) no-repeat 0 0;
}
#clocks .moscow {
    left: 0;
}
#clocks .newyork {
    left: 53px;
}
#clocks .tokyo {
    left: 105px;
}

(function () {
    var dateObj = new Date();
    var m = dateObj.getMinutes(), h = dateObj.getHours() + (dateObj.getTimezoneOffset() + m) / 60;

Переменные m и h содержат минуты и часы соответственно. Более того, значение часов приведено к GMT и скорректировано в зависимости от количества минут.

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


    var minute = Math.floor((m + 1.25) / 2.5) % 24;

    function getHourForCity(h, offset) {
        return Math.floor((12 + h + offset + 0.25) / 0.5) % 24;
    }

Волшебная функция, которая позволит нам определить используется ли у пользователя «летнее» время в системе или нет.


    function daylightSaving() {
        var now = new Date(),
            nowTZ = now.getTimezoneOffset(),
            winterTZ = new Date(now.getFullYear(), 1, 1).getTimezoneOffset(),
            summerTZ = new Date(now.getFullYear(), 7, 1).getTimezoneOffset();
        return (winterTZ !== summerTZ) && (nowTZ === summerTZ) ? 1 : 0;
    }

    var recovery = daylightSaving();

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


    $('#clocks .minute').css('backgroundPosition', 'left -' + 45 * minute + 'px');
    $('#clocks .mosсow .hour').css('backgroundPosition', 'left -' + 45 * getHourForCity(h, 3 + recovery) + 'px');
    $('#clocks .newyork .hour').css('backgroundPosition', 'left -' + 45 * getHourForCity(h, -5 + recovery) + 'px');

В Японии не переводят стрелки на «летнее» время, по этому коррекция часового пояса не требуется.


    $('#clocks .tokyo .hour').css('backgroundPosition', 'left -' + 45 * getHourForCity(h, 9) + 'px');

Обновляем положение стрелок каждую минуту. Мне очень нравится этот шаблон – замыкание + setTimeout, потому что он позволяет выполнить функцию сразу, чего не может обеспечить setInterval.

    setTimeout(arguments.callee, 60000);
})();