Привет, читатель!
Я даже не знаю что хуже, что код для этого секундомера занимает ~ 1100 строк, или то что он работает только в половине браузеров.
Но если же тебе интересно как это работает, прошу под кат.
Вступление
Во-первых о браузерах. Работает этот код, из нынешних: в FireFox, Safari и Chrome. На подходе такие гиганты браузеры как IE10 и супер секретная новая Opera.
Во-вторых, хочу сразу отметить, что я буду описывать именно процесс анимации, а не рисования на 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; }
Запускаем стрелку аналогично с одометром.
Приводить код думаю лишнее.
В общем, финальный результат, еще раз.
Спасибо за внимание, нового я по сути ничего не рассказал, лично я этой инфой владею уже давно.
Просто кому-нибудь это, возможно, будет полезно, или просто интересно.
Автор: leoni4