Привет, Хабраюзер!
Я даже не знаю что хуже, что код для этого секундомера занимает ~ 1100 строк, или то что он работает только в половине браузеров.
Но если же тебе интересно как это работает, прошу под кат.
Вступление
Во-первых о браузерах. Работает этот код, из нынешних: в FireFox, Safari и Chrome. На подходе такие
Во-вторых, хочу сразу отметить, что я буду описывать именно процесс анимации, а не рисования на CSS3. На будущее план как раз наоборот сделать очень красивый рисунок, на CSS. O да, это стало не так нудно, после того как разработчики из сафари отказались от своего старого стандарта linear-gradient'a (прощай color-stop, from, to, 0 0) и Opera наконец-то стала поддерживать radial-gradient (последнее произошло относительно давно).
Сначала я подумал что сделаю всем привычный секундомер, со стрелочками и только, но это очень скучно.
Нет того драйва, и ощущения механизма, когда ты лицезришь две стрелочки, бегающие по монитору. С другой же стороны я сразу вспомнил, как когда-то делал что-то вроде одометра, для пройденного мышкой пути. И меня осенило.
Понеслась
Первым делом выстраиваем HTML (ниже приведен пример, только для «одометра»).
<div class="timer">
<div class="numb tenHour">
</div>
<div class="numb hour">
</div>
<div class="numb tenMin">
</div>
<div class="numb min">
</div>
<div class="numb tenSec">
</div>
<div class="numb sec">
</div>
<div>
:</div>
<div class="numb tenMilisec">
</div>
<div class="numb milisec">
</div>
</div>
План такой: каждому диву numb мы присваиваем псевдоэлемент с записью «0 1 2 3 4 5 6 7 8 9 0».
Почему два нуля? Анимация проходит от одного края к другому, а потом без задержки времени переходит в изначальное положение. По сути мы видим два разных нуля, но на живом примере этого не заметно.
Почему именно псевдоэлемент? Почему не написать числа прямо в див? Тут есть два плюса, во первых мы не портим вид HTML, а во вторых мы пишем это только один раз.
Получаем какой никакой CSS
.timer div
{
float: left;
width: 30px;
height: 30px;
position: relative;
}
.numb::before
{
content:"0 1 2 3 4 5 6 7 8 9 0";
position: absolute;
width: 30px; /* Так как мы поставили ширину 30px, все числа будут с новой строчки, это нам и нужно. */
height: 360px;
color: #334;
text-align: center;
}
.timer div::after
{
content: "";
height: 360px;
width: 1px;
left: 0;
background: #889;
}
.timer div:first-child::after
{
display: none;
}
Ставим у родителя overflow: hidden и вуаля! Все, одометр готов.
Анимация
Приступим же к анимации, тут все просто:
.numb
{
animation-delay: 0; /* Обнуляем задержку анимации */
animation-iteration-count: infinite; /* Количество повторений анимации - бесконечно */
animation-timing-function: linear; /* отображение анимации относительно времени - плавно без рывков */
}
.tenHour {animation-duration: 1000000s;} /* по убывающей количество секунд на анимацию */
.hour { animation-duration: 100000s;}
.tenMin { animation-duration: 10000s;}
.min { animation-duration: 1000s;}
.tenSec { animation-duration: 100s;}
.sec { animation-duration: 10s;}
.tenMilisec { animation-duration: 1s;}
.milisec { animation-duration: .1s;} /* вообще, разрешается использовать ms, но я решил использовать десятую чать секунды */
Теперь самое скучное, фреймы, точнее ключевые фреймы, они же «кадры».
@-keyframes timer
{
0% {top:0}
100% {top:-300px;}
}
Такой код нас не устраивает, все передвигается плавно, но это некрасиво и неправильно, т.к. при паузе мы вполне себе можем попасть на непонятное значение. Пишем так:
@-keyframes timer
{
0% {top:0}
9% {top:0}
10% {top:-30px}
19% {top:-30px}
20% {top:-60px}
29% {top:-60px}
30% {top:-90px}
39% {top:-90px}
40% {top:-120px}
49% {top:-120px}
50% {top:-150px}
59% {top:-150px}
60% {top:-180px}
69% {top:-180px}
70% {top:-210px}
79% {top:-210px}
80% {top:-240px}
89% {top:-240px}
90% {top:-270px}
99% {top:-270px}
100% {top:-300px;}
}
Все расписывать не буду, смысла в этом нет. Суть в том что мы берем 1% от 10-ти секунд, как время отведенное для перелистывания счетчика.
Поэтому для десятых — 0% 9.9% 10% 19.9% и так далее. Дальше больше, сотые — 0% 9.99%, тысячные — 0% 9.999%.
Заработало!
Теперь самое вкусное, как без JS заставить все это двигаться? Как нам имитировать onclick?
Есть несколько приемов, которые отвечают нашим требованиям.
Первый, самый старый и простой — псевдоклассы для ссылки
Я имею ввиду :active, :focus. Но это скучно, да и к тому же багнуто работает в Chrome (Что?! Может быть в IE?! Нет именно в Chrome)
Второй более веселый способ, использовать
<input />
radio и checkbox.И его псевдоелемент :cheked. Этот способ я вычеркнул, потому что нужен будет лишний элемент label для каждого
<input />
Третий способ который пришел ко мне в голову — это та же ссылка, но с использованием :target.
Это не все способы которым можно прибегнуть, но когда я остановился на :target больше думать и не хотелось.
Итак код HTML:
<a class="start" id="start" href="#start"></a>
<a class="pause" id="pause" href="#pause"></a>
<a class="stop" id="stop" href="#stop"></a>
Тут даже обьяснять ничего не надо.
И теперь CSS:
.start:target ~ .timerInner .numb, .pause:target ~ .timerInner .numb
{
animation-name: timer;
}
.start:target ~ .timerInner .numb.tenSec, .pause:target ~ .timerInner .numb.tenSec
{
animation-name: timertenSec;
}
.pause:target ~ .timerInner .sec,
.pause:target ~ .timerInner .tenMilisec
{
animation-play-state: paused;
}
.stop:target ~ .timer .tenSec,
.stop:target ~ .timer .sec,
{
animation-name: reset;
}
Мы ставим в качестве имени анимации те имена кеифреймов, которые давали выше.
И все начинает работать. Для кнопки Пауза тоже ставим, это для того что бы анимация не обнулялась при нажатии на нее, но анимацию нужно остановить, для этого и предусмотрен animation-play-state
При нажатии на Стоп, по идее можно присвоить любое имя, главное чтобы оно не совпадало с именем наших анимаций, тогда произойдет обнуление. Я представил далеко не весь код анимации, дело в том что он однообразен, и присваивается каждому диву отдельно, кому интересно глянет код.
Ок, теперь сделаем часики. Именно стрелку, циферблат нам не интересен.
HTML
<div class="clock">
<div class="line one"></div>
<div class="line three"></div>
<div class="line five"></div>
<div class="arrow"></div>
</div>
Дивы line просто обозначают вертикаль по которой мы ставим деления на минуты.
Arrow и есть наша стрелка.
CSS
.arrow
{
animation-delay: 0; /* Задержка ноль */
animation-iteration-count: infinite; /* бесконечно */
animation-timing-function: linear; /* без рывков */
animation-duration: 600s; /* 10м x 60с = 600с, время анимации */
background:#666;
}
Запускаем стрелку аналогично с одометром.
Приводить код думаю лишнее.
В общем, финальный результат, еще раз.
Спасибо за внимание, нового я по сути ничего не рассказал, лично я этой инфой владею уже давно.
Просто кому-нибудь это, возможно, будет полезно, или просто интересно.