Опрос
Вы участвуете в программе Windows Insider?
Популярные новости
Обсуждаемые новости

25.02.2013 14:43 | dronov_va

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

Эта статья будет посвящена трансформационной анимации с ключевыми кадрами. Её можно рассматривать как дальнейшее развитие уже знакомой нам "простой" трансформационной анимации. Используя анимацию с ключевыми кадрами, мы можем реализовывать более сложные перемещения элементов по странице или более сложные законы изменения их параметров.

Анимация поддерживается Internet Explorer, начиная с версии 10.


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

Самое первое из этих понятий - временная линия. Эта воображаемая линия представляет саму нашу анимацию с виде временного отрезка. Длина этой линии равна времени, в течение которого выполняется анимация; её начало соответствует начальному состоянию анимируемого элемента (началу анимации), а конечная - его конечному состоянию, в котором он окажется по окончанию анимации. Единицы изменения, используемые для измерения временной линии, - это единицы измерения времени CSS3: секунды или миллисекунды (подробнее о них говорилось в статье, посвящённой трансформационной анимации).

Следующее понятие - ключевой кадр (в документации MSDN их называют опорными). Его можно рассматривать как точку на временной линии, задающую параметры анимируемого элемента. Или, говоря другими словами, если нам нужно, чтобы анимируемый элемент в данном месте анимации имел такие-то параметры, мы создадим в нужной точке ключевой кадр с описанием этих параметров.

Анимация должна включать, по меньшей мере, два ключевых кадра, описывающих начальное и конечное состояния анимируемого элемента. Максимальное количество ключевых кадров в анимации не ограничено.

На заметку
Рассмотренную нами в предыдущей статье "простую" трансформационную анимацию можно рассматривать как вырожденный случай анимации с ключевыми кадрами, имеющей лишь два ключевых кадра.

Создание анимации с ключевыми кадрами выполняется в два этапа. На первом этапе мы создаём набор ключевых кадров и описываем в каждом из них параметры, которые должен получить анимируемый элемент на данной фазе анимации. На втором этапе мы указываем параметры самой анимации - её продолжительность, созданный ранее набор ключевых кадров и, возможно, некоторые другие (такие, как её закон или задержку перед её началом), - формируя тем самым временную линию. Для этого используются специализированные атрибуты стиля CSS3, который мы обязательно рассмотрим.


2. Создание анимации с ключевыми кадрами
Теперь рассмотрим сами этапы создания анимации с ключевыми кадрами и все нужные нам атрибуты стиля CSS3.


2.1. Создание набора ключевых кадров
Для создания наборов ключевых кадров применяется особое правило CSS3 под названием @keyframes. (Правилами CSS называются вспомогательные инструкции, не описывающие собственно оформление элементов страницы, но задающие некоторые дополнительные параметры.) Формат его записи таков:

@keyframes <имя набора ключевых кадров> {
  <список ключевых кадров>
}

ключевых кадров>
}[/code]
Уясним сразу, что каждый набор ключевых кадров, описывающих анимацию, должен иметь уникальное имя. Мы укажем это имя в описании собственно анимации, чтобы сослаться на нужный нам набор кадров.

Также не забываем, что правило @keyframes, как и вообще все правила CSS, записывается отдельно от всех стилей. Если вложить правило в какой-либо стиль, оно не будет обработано.

Сам набор ключевых кадров описывается внутри фигурных скобок и представляет собой список описаний всех входящих в него ключевых кадров. Описание одного ключевого кадра формируется согласно следующему формату:

[code]<местоположение на временной линии> {
<список атрибутов стиля, задающих текущие параметры анимируемого элемента>
}[/code]
Местоположение ключевого кадра на временной линии записывается в процентах и отсчитывается от начала анимации. Например, первый ключевой кадр, описывающий начальное состояние анимируемого элемента, будет иметь местоположение 0%, а последний, что задаст конечное состояние элемента, - местоположение 100%. Если нам потребуется создать клчюевой кадр в центре временной линии, мы укажем для него местоположение 50%.

Комитет W3C рекомендует вместо значений местоположения 0% и 100% использовать ключевые слова from и to. Так удобнее.

Что касается параметров, которые анимируемый элемент примет в данном ключевом кадре, то они записываются давно знакомым нам способом - в виде значений различных атрибутов стиля. Эти атрибуты помещаются в фигурных скобках.

Описания отдельных ключевых кадров должны отделяться друг от друга пробелом или символом возврата каретки (как и описания отдельных стилей).

Давайте для примера рассмотрим вот такой набор ключевых кадров, имеющий имя simpleScaling и включающий три кадра:

[code]@keyframes simpleScaling {
from {
transform: scale(1, 1);
}
33% {
transform: scale(2.0, 2.0);
}
to {
transform: scale(1.2, 1.2);
}
}[/code]
Перед началом анимации элемент будет иметь свой изначальный масштаб (первый ключевой кадр), после чего быстро, в течение первой трети общей продолжительности анимации, увеличится вдвое (второй ключевой кадр), а потом медленно уменьшится до масштаба, несколько больше больше изначального (третий ключевой кадр).

Описания первого и последнего ключевого кадра могут быть опущены. В таком случае для первого ключевого кадра браузер возьмёт текущие значения анимируемых параметров, а для последнего попытается найти их в других стилях, применённых к данному элементу. Результат этих поисков может оказаться непредсказуемым, поэтому в наборах рекомендуется описывать все ключевые кадры.


2.2. Создание самой анимации
Описание собственно анимации указывается в стиле, который применяется к элементу. Для этого применяются два ключевых атрибута стиля CSS3, которые мы сейчас рассмотрим.

Первый атрибут стиля - animation-name. Он задаёт имя набора ключевых кадров, который будет использован для создания анимации.

[code]#element:hover {
animation-name: simpleScaling;
}[/code]
После наведения курсора мыши на элемент с именем element к нему будет применена анимация, формируемая на основе созданного нами ранее набора ключевых кадров simpleScaling.

В качестве значения атрибута стиля animation-name мы можем указать несколько наборов ключевых кадров, перечислив их через запятую. В этом случае к элементу будет применена сложная анимация, сформированная на основе всех указанных нами наборов ключевых кадров.

[code]#element:hover {
animation-name: scaling, moving, colorTransition;
}[/code]
Здесь мы применяем к элементу element сразу три набора ключевых кадров.

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

Значение none атрибута стиля animation-name отменяет анимацию.

Второй атрибут стиля - animation-duration. Он задаёт продолжительность анимации.

[code]#element:hover {
animation-duration: 2s;
}[/code]
Задаём продолжительность анимации, равную двум секундам.

Если мы применяем к анимируемому элементу сразу несколько наборов ключевых кадров, то можем указать продолжительность анимации для каждого из них, просто перечислив значения времени через запятую.

[code]#element:hover {
animation-duration: 1s, 500ms, 2s;
}[/code]
Анимация, создаваемая первым набором ключевых кадров, будет продолжаться одну секунду, анимация, создаваемая вторым набором, - полсекунды (500 миллисекунд), а анимация, создаваемая третьим набором, - две секунды.

Если в атрибуте стиля animation-duration указано меньше значений, чем наборов ключевых кадров в animation-name, набор указанных значений продолжительности будет повторяться столько раз, чтобы "покрыть" все указанные наборы. Если же указать в атрибуте стиля animation-duration большее количество значений, лишние значения будут проигнорированы. Как видим, эти атрибуты стиля ведут себя так же, как знакомые нам по предыдущей статье атрибуты стиля transition-property и transition-duration.


2.3. Примеры анимации с ключевыми кадрами
А теперь - собственно практика.

[code]@keyframes simpleScaling {
from {
transform: scale(1, 1);
}
33% {
transform: scale(0.5, 0.5);
}
to {
transform: scale(0.8, 0.8);
}
}
#div1 {
background-color: yellow;
position: absolute;
left: 200px;
top: 200px;
width: 100px;
height: 100px;
cursor: pointer;
}
#div1:hover {
animation-name: simpleScaling;
animation-duration: 2s;
}
. . .
<div id="div1">Блок</div>[/code]
Этот код (служебные теги опущены) создаёт свободно позиционируемый блок div1 и задаёт для него жёлтый цвет фона и форму курсора в виде "указующего перста" - для удобства. При наведении курсора мыши этот блок сначала резко "съёжится" вдвое, после чего медленно увеличится до размеров, чуть меньших, чем изначальные.

[code]@keyframes simpleScaling {
from {
transform: scale(1, 1);
}
33% {
transform: scale(0.5, 0.5);
}
to {
transform: scale(0.8, 0.8);
}
}
@keyframes disappearing {
from {
opacity: 1;
}
50% {
opacity: 1;
}
to {
opacity: 0;
}
}
#div1 {
background-color: yellow;
position: absolute;
left: 200px;
top: 200px;
width: 100px;
height: 100px;
cursor: pointer;
}
#div1:hover {
animation-name: simpleScaling, disappearing;
animation-duration: 2s;
}
. . .
<div id="div1">Блок</div>[/code]
А здесь мы создали анимацию из двух наборов ключевых кадров. Первый набор аналогичен тому, что мы создали в предыдущем примере. Второй же набор задаёт плавное исчезновение элемента, начиная с середины временной шкалы; это достигается плавным изменением значения атрибута стиля opacity, задающего степень непрозрачности элемента, с 1 до 0.

[code]@keyframes colorAndFontSize {
from {
background-color: yellow;
color: black;
font-size: 14px;
}
25% {
background-color: red;
color: blue;
}
50% {
background-color: green;
color: red;
font-size: 96px;
}
75% {
background-color: blue;
color: green;
}
to {
background-color: black;
color: white;
font-size: 40px;
}
}
body {
animation-name: colorAndFontSize;
animation-duration: 20s;
}
. . .
<p>Анимируем параметры тела страницы<p>[/code]
А этот пример создаёт анимацию длительностью 20 секунд, плавно меняющую цвет текста и фона и размер шрифта для всего тела страницы (тега <body>).

Отметим, что во втором и чётвёртом ключевом кадрах атрибут стиля font-size, задающий размер шрифта, не указан. Следовательно, в данных ключевых кадрах значение данного атрибута стиля будет продолжать изменяться от значения, заданного в предыдущем ключевом кадре, к значению, что указано в следующем ключевом кадре. Отметим это на будущее.


3. Дополнительные параметры анимации
Дополнительные параметры, которые мы можем задать для анимации с ключевыми кадрами, можно разделить на три группы. Рассмотрим их по очереди.

Внимание!
Все атрибуты стиля, что будут рассмотрены здесь и далее, подчиняются тем же правилам, что и знакомые нам атрибуты стиля animation-name и animation-duration.


3.1. Закон анимации и задержка перед её началом
Первая группа включает два атрибута стиля, указывающие закон анимации и задержку перед её началом.


3.1.1. Закон анимации
Закон анимации, то есть функция, согласно которой выполняется изменение значений "анимируемых" параметров элемента, задаётся атрибутом стиля animation-timing-function. Он поддерживает следующие значения:

  • ease - в начале скорость анимации слегка увеличивается, а в конце слегка уменьшается. Значение по умолчанию.
  • linear - линейный закон. Анимация выполняется с постоянной скоростью.
  • ease-in - в начале скорость анимации заметно увеличивается, после чего анимация выполняется с постоянной скоростью.
  • ease-out - анимация выполняется с постоянной скоростью, а в конце её скорость заметно уменьшается.
  • ease-in-out - в начале скорость анимации заметно увеличивается, а в конце заметно уменьшается.
  • cubic-bezier - закон, подчиняющийся кубической кривой Безье. Записывается в формате:

    [code]cubic-bezier(
    <горизонтальная координата первой опорной точки>,
    <вертикальная координата первой опорной точки>,
    <горизонтальная координата второй опорной точки>,
    <вертикальная координата второй опорной точки>
    )[/code]
    Все значения должны быть заданы в диапазоне между 0 и 1, где 0 - начало анимации, а 1 - её конец. Однако для всех атрибутов стиля, за исключением color и opacity, вертикальная координата может выходить за эти диапазоны; это может быть использовано для достижения эффекта "эластичной" анимации.

    [code]animation-timing-function: cubic-bezier(0.1, 0.5, 0.9, 0.5);[/code]
  • steps - скачкообразный закон анимации, когда значение "анимируемого" атрибута стиля изменяется не плавно, а скачкообразно - в начале или конце одного из шагов анимации, количество которых задаём мы сами. Записывается в формате:

    [code]steps(
    <количество шагов анимации>,[
    <в каком месте шага анимации выполняется изменение
    значение "анимируемого" атрибута стиля>]
    )[/code]
    Первым значением задаётся количество шагов анимации - в виде числа без указания единицы измерения. Вторым значением указывается, в каком месте шага выполняется изменение значения "анимируемого" атрибута стиля: в его начале (значение start) или в конце (значение end). Если второй параметр не указан, он получит значение end.

    [code]animation-timing-function: steps(10, start);[/code]
    Анимация будет разбита на десять шагов-"скачков", причём значение "анимируемого" атрибута стиля будет изменяться в начале каждого шага.
  • steps-start - "анимируемый" атрибут стиля сразу получит своё конечное значение, которое в дальнейшем не изменится. Эквивалентно указанию steps(1, start).
  • steps-end - "анимируемый" атрибут стиля будет иметь своё начальное значение, которое в конце анимации мгновенно изменится на конечное. Эквивалентно указанию steps(1, end).


[code]body {
animation-timing-function: linear;
}[/code]
Задаём для третьего примера из параграфа 2.3 линейный закон анимации.

[code]#element:hover {
animation-timing-function: ease-in, ease-out;
}[/code]
Задаём для первого набора ключевых кадров закон ease-in, а для второго - закон ease-out.


3.1.2. Задержка перед началом анимации
Задержка перед началом анимации задаётся при помощи атрибута стиля animation-delay.

[code]@keyframes simpleScaling {
from {
transform: scale(1, 1);
}
33% {
transform: scale(0.5, 0.5);
}
to {
transform: scale(0.8, 0.8);
}
}
@keyframes disappearing {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#div1 {
background-color: yellow;
position: absolute;
left: 200px;
top: 200px;
width: 100px;
height: 100px;
cursor: pointer;
}
#div1:hover {
animation-name: simpleScaling, disappearing;
animation-duration: 2s, 1s;
animation-delay: 0s, 1s;
}
. . .
<div id="div1">Блок</div>[/code]
Здесь мы переписали второй пример из параграфа 2.3, использовав атрибут стиля animation-delay. Мы указали для второй анимации секундную задержку, уменьшили её продолжительность также до одной секунды и получили тот же самый результат.


3.2. Режим воспроизведения анимации
Вторая группа устанавливает режим воспроизведения анимации: количество её повторений, порядок воспроизведения (прямой или обратный) и её текущее состояние (воспроизводится ли она в данный момент или приостановлена).


3.2.1. Количество повторений анимации
По умолчанию анимация по ключевым кадрам воспроизводится один-единственный раз. Однако мы можем укажать ей воспроизвестись несколько раз подряд или даже вообще "зациклить", сделав бесконечной.

Всем этим управляет атрибут стиля animation-iteration-count. Его значением может быть как количество повторений анимации, заданное в виде числа без указания единицы измерения, или слово infinite, указывающее брацзеру воспроизводить анимацию бесконечно, "зациклив" её. Значение данного атрибута стиля по умолчанию - 1.

[code]body {
animation-name: colorAndFontSize;
animation-duration: 20s;
animation-iteration-count: infinite;
}[/code]
Здесь мы взяли стиль, привязанный к тегу <body> из третьего примера, что был представлен в параграфе 2.3, и дополнили его атрибутом стиля animation-iteration-count. Теперь анимация будет воспроизводиться бесконечно.


3.2.2. Направление воспроизведения анимации
По умолчанию анимация воспроизводится в прямом порядке: сначала анимируемый элемент приводится в состояние, описанное в первом ключевом кадре, потом - в состояние, описанное во втором кадре, и т. д. Однако мы можем указать обратный порядок воспроизведения анимации и даже заставить её воспроизводиться то в прямом, то в обратном порядке.

В этом нам поможет атрибут стиля animation-direction. Он поддерживает следующие значения:

  • normal - задаёт прямо порядок воспроизведения анимации (значение по умолчанию);
  • reverse - задаёт обратный порядок воспроизведения анимации (анимируемый элемент сначала приводится в состояние, описываемое последним ключевым кадром, потом - предпоследним и т. д.);
  • alternate - нечётные проходы анимации воспроизводятся в прямом порядке, а чётные - в обратном;
  • alternate-reverse - нечётные проходы анимации воспроизводятся в обратном порядке, а чётные - в прямом.


Внимание!
Значения alternate и alternate-reverse атрибута стиля animation-direction имеет смысл задавать только для анимаций, которые выполняются более одного раза.

[code]body {
animation-direction: alternate;
}[/code]
Теперь нечётные проходы анимации в третьем примере из параграфа 2.3 будут выполняться в прямом порадке, а чётные - в обратном. Получится весьма впечатляющий эффект.


3.2.3. Текущее состояние анимации
CSS3 предоставляет нам механизм, позволяющий приостановить воспроизводящуюся в данный момент анимацию и впоследствии возобновить её воспроизведение. Это атрибут стиля animation-play-state, поддерживающий два значения:

  • running - анимация в данный момент воспроизводится (значение по умолчанию);
  • paused - анимация в данный момент приостановлена.


[code]body {
animation-play-state: running;
}
body:hover {
animation-play-state: paused;
}[/code]
Теперь, если мы наведём курсор мыши на содержимое тела страницы из третьего примера в параграфе 2.3, анимация приостановится. Чтобы возобновить её воспроизведение, нам достаточно убрать курсор.


3.3. Поведение анимации до и после его воспроизведения
Давайте вернёмся к самому первому примеру из параграфа 2.3. Наведём курсор мыши на жёлтый блок и подождём, пока анимация не закончится.

Если хорошенько присмотреться, можно заметить, что в самом конце масштаб блока скачкообразно сменится с того, что был задан в последнем ключевом кадре анимации, на тот, что указан в её первом ключевом кадре. Выглядит это очень некрасиво, так что давайте как-то устраним этот скачок.

В этом нам поможет атрибут стиля animation-fill-mode, единолично занимающий третью из упомянутых ранее групп. Давайте рассмотрим все четыре поддерживаемые им значения во всех подробностях.

  • none - перед началом и после завершения анимации параметры анимируемого элемента получат значения, установленные в привязанных к нему стилях. Параметры, заданные в первом и последнем ключевых кадрах анимации, в расчёт не принимаются. Значение по умолчанию.
  • forwards - по завершения анимации параметры анимируемого элемента получат значения, установленные в последнем (если последний проход анимации был выполнен в прямом порядке) или первом (если он был выполнен в обратном порядке) ключевом кадре анимации.
  • backwards - перед началом анимации параметры анимируемого элемента получат значения, установленные в первом ключевом кадре анимации.
  • both - комбинация значений forwards и backwards.


[code]#div1:hover {
animation-fill-mode: forwards;
}[/code]
Теперь анимируемый блок из первого примера в параграфе 2.3 не будет резко уменьшаться в размерах по завершению анимации.


4. Задание всех параметров анимации одновременно
Атрибут стиля animation позволяет нам задать сразу все параметры анимации за один раз. Формат его записи таков:

[code]animation: <имя набора ключевых кадров> <продолжительность> <закон>
<задержка> <количество повторений> <направление>[/code]
К сожалению, мы не сможем указать здесь текущее состояние анимации и её поведение до и после воспроизведения. Но и это тоже неплохо!

[code]body {
animation: colorAndFontSize 20s linear 0s infinite alternate;
}[/code]
Задаём все параметры анимации для третьего примера из параграфа 2.3.


5. Программное управление анимацией
Осталось расмотреть инструменты, предназначенные для программного управления анимацией: её запуска, прерывания и отслеживания момента её начала и завершения.


5.1. Запуск и прерывание анимации
Чтобы программно запустить анимацию, достаточно привязать к анимируемому элементу стилевой класс, определяющий параметры анимации (набор соответствующих атрибутов стиля). Выполняется это знакомым нам по предыдущей статье способом - присвоением наименования стилевого класса свойству className, которое поддерживается всеми объектами, представляющими элементы страницы. Нужно только помнить, что наименование стиля должно указывается в виде строки и без начальной точки.

[code]var div1 = document.getElementById("div1");
div1.className = "animate";[/code]
Прервать выполняющуюся анимацию тоже несложно. Нужно лишь создать стилевой класс, в котором указать для атрибута стиля animation-name значение none, и привязать его к анимируемому элементу.

[code].cancel {
animation-name: none;
}
. . .
div1.className = "cancel";[/code]

5.2. Отслеживание момента начала и окончания анимации
Часто бывает нужно отследить момент начала и завершения анимации. Для этого стандарт HTML5 предоставляет нам три события, перечисленные далее.

  • animationstart - возникает в начале анимации. Если для анимации была задана задержка, то данное событие возникнет по истечению этой задержки.
  • animationend - возникает по завершению анимации.
  • animationiteration - возникает в начале каждого прохода анимации, если анимация выполняется более одного раза.


Отметим, что все эти события возникают в самом анимируемом элементе.

[code]var div1 = document.getElementById("div1");
div1.addEventListener("animationend", function() {
<выполняем какие-то манипуляции>
}):[/code]
Функции-обработчики всех этих событий в качестве единственного параметра получат экземпляр объекта AnimationEvent, хранящий дополнительные сведения о возникшем событии. Эти сведения хранятся в двух свойствах объекта AnimationEvent:

  • animationName - имя набора ключевых кадров в виде строки;
  • elapsedTime - время, прошедшее с начала анимации. Для события animationstart значение этого свойства не имеет смысла, так как анимация к моменту возникновения этого события ещё не началась.


Кроме того, объект AnimationEvent поддерживает все свойства и методы объекта Event (от которого он, собственно, и порождён).


5.3. Пример программно управляемой анимации
Для практики давайте возьмём первый пример из параграфа 2.3 и переделаем его таким образом, чтобы анимация воспроизводилась и при наведении на жёлтый блок курсора мыши, и при его уводе. Полный HTML-код получившейся страницы приведён далее.

[code]<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Анимация CSS3</title>
<style type="text/css">
@keyframes scalingForward {
from {
transform: scale(1, 1);
}
33% {
transform: scale(0.5, 0.5);
}
to {
transform: scale(0.8, 0.8);
}
}
@keyframes scalingBackward {
from {
transform: scale(0.8, 0.8);
}
to {
transform: scale(1, 1);
}
}
#div1 {
background-color: yellow;
position: absolute;
left: 200px;
top: 200px;
width: 100px;
height: 100px;
cursor: pointer;
}
#div1.forward {
animation-name: scalingForward;
animation-duration: 2s;
animation-fill-mode: forwards;
}
#div1.backward {
animation-name: scalingBackward;
animation-duration: 1s;
}
</style>
</head>
<body>
<div id="div1">Блок</div>
</body>
<script type="text/javascript">
var div1 = document.getElementById("div1");

div1.addEventListener("mouseenter", function() {
div1.className = "forward";
});
div1.addEventListener("mouseleave", function() {
div1.className = "backward";
});
</script>
</html>[/code]
Событие mouseenter возникает при наведении курсора мыши на элемент страницы, а событие mouseleave - при его уводе. В остальном приведённый код особых пояснений не требует.


6. Заключение
CSS3 - поистине кладезь новых возможностей. Всё то, что ранее реализовывалось гигантскими JavaScript-библиотеками и через пень-колоду, ныне делается всего лишь написанием нескольких строк CSS-кода.

Чего ещё желать? Если только скорейшего принятия нового стандарта...


Дополнительные материалы



dronov_va, TheVista.Ru Team
Март 2013

Комментарии

Комментариев нет...
Для возможности комментировать войдите в 1 клик через

По теме

Акции MSFT
420.55 0.00
Акции торгуются с 17:30 до 00:00 по Москве
Все права принадлежат © ms insider @thevista.ru, 2022
Сайт является источником уникальной информации о семействе операционных систем Windows и других продуктах Microsoft. Перепечатка материалов возможна только с разрешения редакции.
Работает на WMS 2.34 (Страница создана за 0.085 секунд (Общее время SQL: 0.062 секунд - SQL запросов: 53 - Среднее время SQL: 0.00117 секунд))
Top.Mail.Ru