Нордавинд
Компания
41,88
рейтинг
18 января 2014 в 14:28

Разработка → Трюки с CSS-анимациями: мгновенные изменения, отрицательные задержки, анимация transform-origin и другое перевод

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

Быстрое изменение состояния посреди анимации

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

@keyframes toggleOpacity {
  50% { opacity: 1; } /* Turn off */
  50.001% { opacity: 0.4; }

  /* Keep off state for a short period */

  52.999% { opacity: 0.4; } /* Turn back on */
  53% { opacity: 1; }
}

Вот как я использовал этот приём для имитации мигающей неоновой вывески с помощью прозрачности и свойства text-shadow:



Код примера:
HTML:

<link href='http://fonts.googleapis.com/css?family=Monoton' rel='stylesheet' type='text/css'>
<div>
  <p id="error">E<span>r</span>ror</p>
  <p id="code">4<span>0</span><span>4</span></p>
</div>

CSS:

body { background-color: #111111; }
div {
  padding: 40px;
  font-size: 75px;
  font-family: 'Monoton', cursive;
  text-align: center;
  text-transform: uppercase;
  text-shadow: 0 0 80px red,0 0 30px FireBrick,0 0 6px DarkRed;
  color: red;
}
div p { margin:0; }
#error:hover { text-shadow: 0 0 200px #ffffff,0 0 80px #008000,0 0 6px #0000ff; }
#code:hover { text-shadow: 0 0 100px red,0 0 40px FireBrick,0 0 8px DarkRed; }
#error {
  color: #fff;
  text-shadow: 0 0 80px #ffffff,0 0 30px #008000,0 0 6px #0000ff;
}
#error span {
  animation: upper 11s linear infinite;
}
#code span:nth-of-type(2) {
  animation: lower 10s linear infinite;
}
#code span:nth-of-type(1) {
  text-shadow: none;
  opacity:.4;
}
@keyframes upper {
  0%,19.999%,22%,62.999%,64%, 64.999%,70%,100% {
    opacity:.99; text-shadow: 0 0 80px #ffffff,0 0 30px #008000,0 0 6px #0000ff;
  }
  20%,21.999%,63%,63.999%,65%,69.999% {
    opacity:0.4; text-shadow: none; 
  }
}
@keyframes lower {
  0%,12%,18.999%,23%,31.999%,37%,44.999%,46%,49.999%,51%,58.999%,61%,68.999%,71%,85.999%,96%,100% {
    opacity:0.99; text-shadow: 0 0 80px red,0 0 30px FireBrick,0 0 6px DarkRed;
  }
  19%,22.99%,32%,36.999%,45%,45.999%,50%,50.99%,59%,60.999%,69%,70.999%,86%,95.999% { 
    opacity:0.4; text-shadow: none; 
  }
}


Демо на Codepen

Отрицательные задержки анимации

Положительная задержка откладывает начало анимации на некоторое время. А отрицательная — начинает анимацию немедленно, но не с самого сначала, а со времени, указанного в задержке. Другими словами, начинает анимацию с какого-то момента внутри её цикла. Это позволяет применять анимацию к нескольким элементам со сдвигом фазы, изменяя лишь время задержки. Вот пример использования такой анимации:



Код примера:
HTML:

<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>

CSS:

div {
  border-radius:50%;
  position:absolute;
  top:50%; left:75%;
}
div:nth-of-type(odd) { background:black; }
div:nth-of-type(even) { background:white; border:2px solid black; }
div:nth-of-type(11) {
  height:10px; width:10px;
  margin-top:-5px; margin-left:-5px;
  -webkit-animation:slide 3s ease-in-out infinite;
  animation:slide 3s ease-in-out infinite;
}
div:nth-of-type(10) {
  height:20px; width:20px;
  margin-top:-12px; margin-left:-12px;
  -webkit-animation:slide 3s -2.7s ease-in-out infinite;
  animation:slide 3s -2.7s ease-in-out infinite;
}
div:nth-of-type(9) {
  height:40px; width:40px;
  margin-top:-20px; margin-left:-20px;
  -webkit-animation:slide 3s -2.4s ease-in-out infinite;
  animation:slide 3s -2.4s ease-in-out infinite;
}
div:nth-of-type(8) {
  height:60px; width:60px;
  margin-top:-32px; margin-left:-32px;
  -webkit-animation:slide 3s -2.1s ease-in-out infinite;
  animation:slide 3s -2.1s ease-in-out infinite;
}
div:nth-of-type(7) {
  height:80px; width:80px;
  margin-top:-40px; margin-left:-40px;
  -webkit-animation:slide 3s -1.8s ease-in-out infinite;
  animation:slide 3s -1.8s ease-in-out infinite;
}
div:nth-of-type(6) {
  height:100px; width:100px;
  margin-top:-52px; margin-left:-52px;
  -webkit-animation:slide 3s -1.5s ease-in-out infinite;
  animation:slide 3s -1.5s ease-in-out infinite;
}
div:nth-of-type(5) {
  height:120px; width:120px;
  margin-top:-60px; margin-left:-60px;
  -webkit-animation:slide 3s -1.2s ease-in-out infinite;
  animation:slide 3s -1.2s ease-in-out infinite;
}
div:nth-of-type(4) {
  height:140px; width:140px;
  margin-top:-72px; margin-left:-72px;
  -webkit-animation:slide 3s -0.9s ease-in-out infinite;
  animation:slide 3s -0.9s ease-in-out infinite;
}
div:nth-of-type(3) {
  height:160px; width:160px;
  margin-top:-80px; margin-left:-80px;
  -webkit-animation:slide 3s -0.6s ease-in-out infinite;
  animation:slide 3s -0.6s ease-in-out infinite;
}
div:nth-of-type(2) {
  height:180px; width:180px;
  margin-top:-92px; margin-left:-92px;
  -webkit-animation:slide 3s -0.3s ease-in-out infinite;
  animation:slide 3s -0.3s ease-in-out infinite;
}
div:nth-of-type(1) {
  height:200px; width:200px;
  margin-top:-100px; margin-left:-100px;
  -webkit-animation:slide 3s ease-in-out infinite;
  animation:slide 3s ease-in-out infinite;
}
@keyframes slide {
  0% { left:75% }
  50% { left:25%; }
  100% { left:75%; }
}
@-webkit-keyframes slide {
  0% { left:75% }
  50% { left:25%; }
  100% { left:75%; }
}


Демо на Codepen

Пропорциональные анимации

Я стараюсь делать веб-приложения настолько адаптивными, насколько возможно. Это касается и CSS-анимаций. Хотя сделать абсолютно все анимации адаптивными невозможно, иногда всё же удаётся использовать вместо абсолютных единиц измерения проценты и другие относительные единицы.

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

.container {
  position: relative;
  display: block;
  width: 100%;
  height: 0;
  padding-bottom: 100%;
}

Если вы откроете следующий пример на Codepen и попробуете изменить размеры окна, вы увидите как это работает. В этом примере также использованы отрицательные задержки анимации.



Код примера:
HTML:

<span> <!-- Square container -->
  <div></div> <!-- Bars -->
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</span>

CSS:

body { 
  background: rgb(20, 20, 20);
  overflow: hidden;
}
span {
  position: relative;
  display: block;
  width: 100%;
  height: 0;
  padding-top: 100%;
  overflow: hidden;
}
div {
  margin-top: -17%;
  height: 34%; 
  width: 2%;
  top: 30%;
  border-radius: 20px;
  position: absolute;
}
div:nth-of-type(1)  { animation: wave 17s   0.000s linear infinite; }
div:nth-of-type(2)  { animation: wave 17s -16.227s linear infinite; }
div:nth-of-type(3)  { animation: wave 17s -15.455s linear infinite; }
div:nth-of-type(4)  { animation: wave 17s -14.682s linear infinite; }
div:nth-of-type(5)  { animation: wave 17s -13.909s linear infinite; }
div:nth-of-type(6)  { animation: wave 17s -13.136s linear infinite; }
div:nth-of-type(7)  { animation: wave 17s -12.364s linear infinite; }
div:nth-of-type(8)  { animation: wave 17s -11.591s linear infinite; }
div:nth-of-type(9)  { animation: wave 17s -10.818s linear infinite; }
div:nth-of-type(10) { animation: wave 17s -10.045s linear infinite; }
div:nth-of-type(11) { animation: wave 17s  -9.273s linear infinite; }
div:nth-of-type(12) { animation: wave 17s  -8.500s linear infinite; }
div:nth-of-type(13) { animation: wave 17s  -7.727s linear infinite; }
div:nth-of-type(14) { animation: wave 17s  -6.955s linear infinite; }
div:nth-of-type(15) { animation: wave 17s  -6.182s linear infinite; }
div:nth-of-type(16) { animation: wave 17s  -5.409s linear infinite; }
div:nth-of-type(17) { animation: wave 17s  -4.636s linear infinite; }
div:nth-of-type(18) { animation: wave 17s  -3.864s linear infinite; }
div:nth-of-type(19) { animation: wave 17s  -3.091s linear infinite; }
div:nth-of-type(20) { animation: wave 17s  -2.318s linear infinite; }
div:nth-of-type(21) { animation: wave 17s  -1.545s linear infinite; }
div:nth-of-type(22) { animation: wave 17s  -0.773s linear infinite; }

@keyframes wave {
  0%   { left:-2%; background: #3B44D1; }
  5%   { background: #9337FE; }
  10%  { height:10%; margin-top: -5%; background: #C532FC; }
  15%  { background: #F639F8; }
  20%  { height:34%; margin-top:-17%; background: #F236C8; }
  25%  { background: #FF2F8D; }
  30%  { height:10%; margin-top: -5%; background: #EE3860; }
  35%  { background: #DC5245; }
  40%  { height:34%; margin-top:-17%; background: #F38643; }
  45%  { background: #F8B435; }
  50%  { height:10%; margin-top: -5%; background: #FAF444; }
  55%  { background: #E0FF3B; }
  60%  { height:34%; margin-top:-17%; background: #E1FF3C; }
  65%  { background: #46F443; }
  70%  { height:10%; margin-top: -5%; background: #54E67B; }
  75%  { background: #4DF3A9; }
  80%  { height:34%; margin-top:-17%; background: #3AF9DA; }
  85%  { background: #36EBF4; }
  90%  { height:10%; margin-top: -5%; background: #3DB3F3; }
  95%  { background: #3C82F1; }
  100% { height:34%; margin-top:-17%; left:100%; background: #5B38EE; }
}


Демо на Codepen

Изменение transform-origin посреди анимации

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



Код примера:
HTML:

<div></div> 

CSS:

div {
  width:200px; height:200px;
  background:rgba(0,0,255,.5);
  animation:flipAround 8s infinite;
}
@keyframes flipAround {
  25% { animation-mode:forwards; transform-origin:right; transform:rotateY(-180deg); }
  25.001% { transform:translateX(200px); }
  50% { transform-origin:bottom; transform:translateX(200px) rotateX(-180deg); }
  50.001% { transform:translateX(200px) translateY(200px); }
  75% { transform-origin:left; transform:translateX(200px) translateY(200px) rotateY(180deg); }
  75.001% { transform:translateY(200px); }
  100% { transform-origin:top; transform:translateY(200px) rotateX(180deg); }
}
body { background:rgb(20,20,20); }


Демо на Codepen

У этого трюка есть недостаток: вы не можете использовать animation-mode: forwards; только для части анимации. Это значит, что нам придется передвигать элемент при каждом изменении transform-origin. В этом примере translate используется для имитации эффектов поворота. В более сложных примерах это может оказаться довольно утомительным.

Отрицательный transform-origin

Можно задать отрицательное значение transform-origin, что может быть полезно для создания вращающихся элементов. Вместо того, чтобы отдельно задавать смещение и угол поворота для элемента, как описывает Ли Веру, этого можно добиться проще, используя отрицательные значения transform-origin и дополнительный элемент или псевдоэлемент (или только один элемент, если он не обязан сохранять постоянный угол относительно горизонта). С разными значениями transform-origin можно использовать одну и ту же анимацию для разных элементов:



Код примера:
HTML:

<div></div>

CSS:

div { width:100px; height:100px; margin-top:180px; transform-origin:200% center; position:relative; animation:rotate 3s linear infinite; }
div:before { content:''; position:absolute; height:100%; width:100%; background:blue; animation:rotate 3s linear reverse infinite; }
@keyframes rotate { 100% { transform:rotate(-360deg); } }

body { background:rgb(20,20,20); } /* The best color ever */


Демо на Codepen

Магия box-shadow

Для анимации простых форм без контента внутри может пригодиться свойство box-shadow. С его помощью можно создавать несколько границ вокруг одного элемента. На основе этой идеи, используя разные смещения для теней, можно создавать целые наборы анимированных фигур на основе одного элемента HTML. Вот пример анимации, которая выглядит, как шесть вращающихся кружков, которые целиком сделаны с помощью box-shadow:



Код примера:
HTML:

<div></div>

CSS:

div {
  border-radius:50%;
  height:2px; width:2px; /* To allow border-radius to work */
  position:absolute;
  top:50%; left:50%;
  margin-top:-1px; margin-left:-1px;
  box-shadow:
    -75px -125px 0 40px #6cce74,
     75px -125px 0 40px #c18d46,
    150px    0px 0 40px #c14745,
     75px  125px 0 40px #2e1e5b,
    -75px  125px 0 40px #9c37a6,
   -150px    0px 0 40px #76bdd1;
  -webkit-animation:rotate 12s infinite linear;
  animation:rotate 12s infinite linear;
}
@keyframes rotate {
  16.67% {
    box-shadow:
      -75px -125px 0 40px #76bdd1,
       75px -125px 0 40px #6cce74,
      150px    0px 0 40px #c18d46,
       75px  125px 0 40px #c14745,
      -75px  125px 0 40px #2e1e5b,
     -150px    0px 0 40px #9c37a6;
  }
  33.33%   { 
    box-shadow:
      -75px -125px 0 40px #9c37a6,
       75px -125px 0 40px #76bdd1,
      150px    0px 0 40px #6cce74,
       75px  125px 0 40px #c18d46,
      -75px  125px 0 40px #c14745,
     -150px    0px 0 40px #2e1e5b;
  }
  50%      { 
    box-shadow:
      -75px -125px 0 40px #2e1e5b,
       75px -125px 0 40px #9c37a6,
      150px    0px 0 40px #76bdd1,
       75px  125px 0 40px #6cce74,
      -75px  125px 0 40px #c18d46,
     -150px    0px 0 40px #c14745;
  }
  66.67%   { 
    box-shadow:
      -75px -125px 0 40px #c14745,
       75px -125px 0 40px #2e1e5b,
      150px    0px 0 40px #9c37a6,
       75px  125px 0 40px #76bdd1,
      -75px  125px 0 40px #6cce74,
     -150px    0px 0 40px #c18d46;
  }
  88.88%   { 
    box-shadow:
      -75px -125px 0 40px #c18d46,
       75px -125px 0 40px #c14745,
      150px    0px 0 40px #2e1e5b,
       75px  125px 0 40px #9c37a6,
      -75px  125px 0 40px #76bdd1,
     -150px    0px 0 40px #6cce74;
  }
  100% {
    transform:rotate(-360deg);
    box-shadow:
      -75px -125px 0 40px #6cce74,
       75px -125px 0 40px #c18d46,
      150px    0px 0 40px #c14745,
       75px  125px 0 40px #2e1e5b,
      -75px  125px 0 40px #9c37a6,
     -150px    0px 0 40px #76bdd1;
  }
}
@-webkit-keyframes rotate {
  16.67% {
    box-shadow:
      -75px -125px 0 40px #76bdd1,
       75px -125px 0 40px #6cce74,
      150px    0px 0 40px #c18d46,
       75px  125px 0 40px #c14745,
      -75px  125px 0 40px #2e1e5b,
     -150px    0px 0 40px #9c37a6;
  }
  33.33%   { 
    box-shadow:
      -75px -125px 0 40px #9c37a6,
       75px -125px 0 40px #76bdd1,
      150px    0px 0 40px #6cce74,
       75px  125px 0 40px #c18d46,
      -75px  125px 0 40px #c14745,
     -150px    0px 0 40px #2e1e5b;
  }
  50%      { 
    box-shadow:
      -75px -125px 0 40px #2e1e5b,
       75px -125px 0 40px #9c37a6,
      150px    0px 0 40px #76bdd1,
       75px  125px 0 40px #6cce74,
      -75px  125px 0 40px #c18d46,
     -150px    0px 0 40px #c14745;
  }
  66.67%   { 
    box-shadow:
      -75px -125px 0 40px #c14745,
       75px -125px 0 40px #2e1e5b,
      150px    0px 0 40px #9c37a6,
       75px  125px 0 40px #76bdd1,
      -75px  125px 0 40px #6cce74,
     -150px    0px 0 40px #c18d46;
  }
  88.88%   { 
    box-shadow:
      -75px -125px 0 40px #c18d46,
       75px -125px 0 40px #c14745,
      150px    0px 0 40px #2e1e5b,
       75px  125px 0 40px #9c37a6,
      -75px  125px 0 40px #76bdd1,
     -150px    0px 0 40px #6cce74;
  }
  100% {
    -webkit-transform:rotate(-360deg);
    box-shadow:
      -75px -125px 0 40px #6cce74,
       75px -125px 0 40px #c18d46,
      150px    0px 0 40px #c14745,
       75px  125px 0 40px #2e1e5b,
      -75px  125px 0 40px #9c37a6,
     -150px    0px 0 40px #76bdd1;
  }
}


Демо на Codepen

К сожалению, box-shadow не поддерживает относительные размеры в процентах, так что их труднее сделать адаптивными, чем обычные элементы. Тем не менее, их размеры можно менять вручную или применяя transform:scale(n) для родительского элемента.

Использование псевдоэлементов

Так же как и box-shadow, псевдоэлементы можно использовать для обогащения внешнего вида элементов HTML. Для них можно использовать отдельные от родительского элемента анимации, они могут иметь отдельные тени — практически как настоящие элементы. Это позволяет делать удивительные вещи:



Код примера:
HTML:

<div id='gif'></div>

CSS:

body { background:black; overflow:hidden; }
#gif {
  background:black;
  padding:10px;
  height:80px;
  width:80px;
  border-radius:50%;
  position:absolute;
  top:calc(50% - 40px);
  left:calc(50% - 40px);
  box-shadow:
    178px 0 0 -25px black,
    178px 0 0 -20px white,
    -178px 0 0 -25px black,
    -178px 0 0 -20px white, 
    0 0 0 20px black,
    0 0 0 30px white,
    0 0 0 130px black,
    0 0 0 135px white;
  -webkit-animation: rotate 3s linear infinite;
  animation: rotate 3s linear infinite;
}
#gif:before {
  content: " ";
  position: absolute;
  height:50px;
  width:50px;
  border-radius:50%;
  top: -155px;
  left: 20px;
  background:black;
  border: 5px solid white;
  box-shadow:
    0 355px 0 -5px black,
    0 355px 0 0px white;
  -webkit-animation: reverseRotate 3s linear infinite;
  animation: reverseRotate 3s linear infinite;
}
#gif:after {
  /* Segmented circle code goes here */
  content: " ";
  position: absolute;
  height:280px;
  width:280px;
  left:-90px;
  top:-90px;
  background-image: url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSAiaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDEwMCAxMDAiID4NCiAgICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlLWRhc2hhcnJheT0iMC45NTIiIHN0cm9rZS13aWR0aD0iOCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIvPg0KPC9zdmc+");
  background-repeat: no-repeat;
  z-index:2;
  -webkit-animation: segmentRotate 300s linear infinite;
  animation: segmentRotate 300s linear infinite;
}
@keyframes rotate {
  0%   { transform: rotate(  0deg); }  
  10%, 15%, 35%, 40%, 60%, 65%, 85%, 90%  { 
    background: black; 
    box-shadow:
      178px 0 0 -25px black,
      178px 0 0 -20px white,
      -178px 0 0 -25px black,
      -178px 0 0 -20px white, 
      0 0 0 20px black,
      0 0 0 30px white,
      0 0 0 130px black,
      0 0 0 135px white;
  }
  12.5%, 37.5%, 62.5%, 87.5% { 
    background: white; 
    box-shadow:
      178px 0 0 -25px white,
      178px 0 0 -20px white,
      -178px 0 0 -25px white,
      -178px 0 0 -20px white, 
      0 0 0 20px black,
      0 0 0 30px white,
      0 0 0 130px black,
      0 0 0 135px white;
  }    
  100% { transform: rotate(360deg); }
}
@keyframes reverseRotate {
  0%   { transform: translateY(178px) rotate(0deg) translateY(-178px) rotate(0deg); }  
  10%, 15%, 35%, 40%, 60%, 65%, 85%, 90%  { 
    background: black; box-shadow: 0 355px 0 -5px black, 0 355px 0 0px white; 
  }
  12.5%, 37.5%, 62.5%, 87.5% { 
    background: white; box-shadow: 0 355px 0 -5px white, 0 355px 0 0px white; 
  }    
  100% { transform: translateY(178px) rotate(-720deg) translateY(-178px) rotate(0deg); }
}
@keyframes segmentRotate {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(-32000deg); }
}


@-webkit-keyframes rotate {
  0%   { -webkit-transform: rotate(  0deg); }  
  10%, 15%, 35%, 40%, 60%, 65%, 85%, 90%  { 
    background: black; 
    box-shadow:
      178px 0 0 -25px black,
      178px 0 0 -20px white,
      -178px 0 0 -25px black,
      -178px 0 0 -20px white, 
      0 0 0 20px black,
      0 0 0 30px white,
      0 0 0 130px black,
      0 0 0 135px white;
  }
  12.5%, 37.5%, 62.5%, 87.5% { 
    background: white; 
    box-shadow:
      178px 0 0 -25px white,
      178px 0 0 -20px white,
      -178px 0 0 -25px white,
      -178px 0 0 -20px white, 
      0 0 0 20px black,
      0 0 0 30px white,
      0 0 0 130px black,
      0 0 0 135px white;
  }    
  100% { -webkit-transform: rotate(360deg); }
}
@-webkit-keyframes reverseRotate {
  0%   { -webkit-transform: translateY(178px) rotate(0deg) translateY(-178px) rotate(0deg); }  
  10%, 15%, 35%, 40%, 60%, 65%, 85%, 90%  { 
    background: black; box-shadow: 0 355px 0 -5px black, 0 355px 0 0px white; 
  }
  12.5%, 37.5%, 62.5%, 87.5% { 
    background: white; box-shadow: 0 355px 0 -5px white, 0 355px 0 0px white; 
  }    
  100% { -webkit-transform: translateY(178px) rotate(-720deg) translateY(-178px) rotate(0deg); }
}
@-webkit-keyframes segmentRotate {
  0%   { -webkit-transform: rotate(0deg); }
  100% { -webkit-transform: rotate(-32000deg); }
}


Демо на Codepen

В этом примере все концентрические окружности вокруг центрального мигающего круга, так же как и два маленьких кружка на внешнем кольце сделаны с помощью box-shadow. Другие два кружка — это тени псевдоэлемента, а кольцо из штрихов — это фон ещё одного псевдоэлемента, заданный в виде inline SVG.

Несколько советов напоследок


Используйте трансформации везде, где только можно

Как показал Пол Айриш и другие, трансформации работают быстрее, чем изменение размеров и положения элементов с помощью свойст top, left, width и height.

С помощью трансформаций легче реализовать адаптивный дизайн, применяя относительные значения для scale (пример).

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

z-index может вызывать проблемы

Пожалуй, на решение проблем с z-index я потратил больше времени, чем на любые другие. Реализация z-index разнится от браузера к браузеру. Главное различие — Mozilla не анимирует z-index, и его значение изменяется скачкообразно, тогда браузеры на основе Webkit умеют изменять его плавно.

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

И последнее. Любой элемент, для которого задана прозрачность, отличная от «1», получает собственный контекст наложения. Подробнее об этом — в статье Филипа Уолтона.

Ищите источники вдохновения

Что-то в реальном мире, интересная веб-страница, необычный видеоэффект, анимированный gif или что угодно ещё — постоянно ищите вещи, которые стоит попробовать реализовать.

Я обнаружил, что если не подглядывать сразу, как именно сделан тот или иной эффект, можно найти уникальный способ и даже превзойти оригинал. Даже если я терплю неудачу, я всегда по крайней мере узнаю что-то новое о языке программирования, который я использую. Часто получается так, что даже не реализованная полностью задумка оказывается довольно эффектной. Иногда наоборот — результат оказывается намного лучше, чем я мог мечтать.

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

Автор: @ilya42 Zach Saucier
Нордавинд
рейтинг 41,88
Компания прекратила активность на сайте

Комментарии (23)

  • +20
    Очень интересные примеры, особенно приятно смотреть 404
    • +1
      Согласен. 404 отлично вышло!

      Хотел сделать лого на своем сайте стилизованно под неоновую вывеску. Сделал это при помощи анимации GIF. Приятно было увидеть реализацию на чистом CSS. Утащил в копилку :-)

      Спасибо!
  • +7
    Да, 404 шикарен. Плюсую в карму за перевод такой интересной статьи.
  • +5
    Кстати, глядя на 404 проностальгировал по Биошоку первому.
  • –1
    Версткой занимаюсь очень давно и я никогда не любил лису, когда в остальных браузерах все ровно, даже в последнем IE то лиса как обычно отчуидит и надо через хак прописывать отдельный ксс.

    Никогда не понимал истерии и фанатизма вокруг лисы, единственный плюс это то что под нее много плагинов, а так: жрет память, подтормаживает там где не должна, требует специальной верстки.
    • +6
      Вы описали любой популярный браузер, а не только Firefox.
      • 0
        Я описал именно Firefox так как проблемы в основном только с ней (IE не в счет)
    • +3
      Файрфокс как раз память не жрет, в отличии от хрома. Файрфокс память кушает вполне умеренно, а вот хром, если ему добавить чуть чуть плагинов, отжирает совершенно запредельные количества памяти на каждую вкладку(что, впрочем, вполне обосновано архитектурой, при которой он по сути запускает копию браузера для каждого домена и как по мне плюсы того стоят, память дешевая).

      Ну и насчет верстки — особо проблем никогда небыло, только на экспериментальных или очень сложных случаях, но там везде проблемы.
      • –1
        Ну в фаерфоксе у объектов DOM-дерева нет поля innerText, когда он есть даже в IE. И такие мелочи иногда всплывают, и если вовремя не заметить — могут доставить много проблем.
        • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Там не совсем копии. Есть расшареная память.
    • 0
      У меня обратная ситуация, в лисе все отображается и работает как ожидалось, под хром _иногда_ надо подправить, в IE и Safari — необъяснимые косяки и отсутствие поддержки необходимых фитч,

      Ласа удобна при работе со множеством вкладок, это оправдывает некоторые недостатки.
  • +2
    css-tricks.com/myth-busting-css-animations-vs-javascript/ Оставлю тут ссылочку — Развенчание мифа о преимуществе CSS анимаций над JS анимацией
    Повод для холивара, имхо.
  • 0
    Всегда нравились такие вещи на CSS. На HTML5-то выглядит захватывающе, а без его использования — просто великолепно!
    • 0
      меня только всегда смущает 5 браузеров — 6 вариантов отображения) особенно в мобильных устройствах)
      • 0
        Да, это, конечно, проблема… Но она всегда есть при разработки веб-приложений.
        • 0
          я к тому что вот такие красивости это как в пословице «Висит груша, нельзя скушать». Вроде бы как концепт супер, а вот применить это так, чтобы все юзеры видели одно и тоже и были довольны, не получится. старые браузеры ужасно тормозят развитие веба. Особенно IE усердствует. Я порой на такое натыкаюсь, что мне кажется если буду рассказывать, то никто не поверит)
          • –2
            Я за жёсткий подход. Заходит такой пользователь на сайт и две ссылки: на Google Chrome и HTML-версия. Или только первую.
            • +2
              как программист программисту: руки бы таким…
              За что?
              Если у меня выключена поддержка JS, то некоторые (и их всё больше) не делают извещения об этом на той же странице тегом noscript, а переадресовывают меня на страницу bad-browser без возможности потом кликнуть «Назад» для попадания на предыдущую, нужную мне, страницу.
              • 0
                Честно говоря, это была скорее шутка, но раз уж на то пошло, то руки надо отрывать тем, кто делает именно так, как вы описали.
                Если бы у меня не было желания поддерживать старые версии, то переадресовывать так жёстко я бы не стал: просто известил бы о вероятности нестабильной работы и предложил бы скачать/обновить браузер для стабильной работы.
  • –1
    Последнее время работаю через oDesk. Ибо с ним не надо думать об инвойсах. Совсем.

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

Самое читаемое Разработка