Сложная CSS анимация — это множество простых решений

Казалось бы, нет ничего проще, чем найти на просторах Сети нехитрый код анимации CSS и анимировать какой-либо объект, благо библиотек анимации предостаточно. Не нужно обладать какими-то сверх-познаниями, чтобы это сделать, код достаточно прост и понятен, даже новичку. Сегодня практически все браузеры, от мала до велика, её поддерживают, и это здорово. Однако чем больше занимаешься веб-дизайном, тем чаще появляется необходимость в индивидуальных решениях и усложнении кода. Вот тут и начинаются проблемы…

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

Для примера попробую простенький код HTML, заодно вспомню «Алису в стране чудес»:

<div class="smile-box">
    <div class="smile"></div>
</div>

Добавлю стили:

body {
    margin: 0;
    padding: 0;
    background: rgb(253, 250, 233);
    display: flex;
    justify-content: center;
}

.smile-box {
    max-width: 1000px;
    width: 100%;
    height: 100px;
    margin-top: 5em;
    background: linear-gradient(rgb(179, 208, 236), rgb(152, 85, 240));
    display: flex;
    align-items: center;
    overflow: hidden;
}

.smile {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background: rgb(250, 173, 101) url(smile.png) no-repeat center center;
    background-size: 75%;
    transform: translateX(-100px);
    box-shadow: inset 0 0 50px rgba(151, 87, 28, 0.7), 0 0 5px rgb(151, 87, 28);
}

Представьте: мне необходимо, чтобы при наведении курсора на родительский контейнер улыбка Чеширского кота «катилась» вправо. Для этого мне понадобится две трансформации: вокруг своей оси и слева-направо.

.smile-box:hover>.smile {
    animation: 3.2s linear infinite reverse spin-to-right;
}

@keyframes spin-to-right {
    0% {
        transform: translateX(1000px) rotate(360deg);
    }
    100% {
        transform: translateX(-100px) rotate(0deg);
    }
}

Анимация прекрасно работает, достаточно навести курсор на дорожку:

See the Pen Complex CSS animation is a lot of simple solutions-1 by haracan (@haracan) on CodePen.

Теперь попробую сделать так, чтобы «улыбка» не просто двигалась вправо по прямой, а двигалась волнообразно или подпрыгивала на лету, как мячик. Попробую в свою анимацию добавить трансформацию по оси Y (translateY) — но код не работает как надо; код не будет работать, даже если разбить одну сложную анимацию на две. Это происходит потому, что все мои трансформации применены к одному объекту. Иногда удаётся что-то настроить, и вроде бы анимация воспроизводится нормально, но стоит открыть страничку в другом браузере, и объект начинает вести себя странно.

Было время, когда в подобных случаях я начинал рвать на себе волосы, и бесконечно правил CSS, и фантазировал, но в конце концов до меня дошло, что дело вовсе не в CSS и не в анимации, проблема в HTML. То есть всё дело в боксе, к которому применяется трансформация. Для корректного воспроизведения анимации необходимо добавить ему «обёртку»:

<div class="smile-box">
    <div class="smile-wrapper">
        <div class="smile"></div>
    </div>
</div>

Далее следует скорректировать код CSS таким образом, чтобы на «обёртку» и «улыбку» приходилось по одной простой анимации:

.smile-wrapper {
    width: 80px;
    height: 80px;
    transform: translateX(-100px);
}

.smile {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background: rgb(250, 173, 101) url(smile.png) no-repeat center center;
    background-size: 75%;
    box-shadow: inset 0 0 50px rgba(151, 87, 28, 0.7), 0 0 5px rgb(151, 87, 28);
}

.smile-box:hover>.smile-wrapper>.smile {
    animation: 3.2s linear infinite reverse spin-to-right;
}

.smile-box:hover>.smile-wrapper {
    animation: 3.2s ease infinite reverse wave;
}

Сделаю оптимальное количество «волн» во второй анимации, ведь расстояние относительно «улыбки» совсем небольшое, но можно сделать и больше.

@keyframes spin-to-right {
    0% {
        transform: translateX(1000px) rotate(720deg);
    }
    100% {
        transform: translateX(-100px) rotate(0deg);
    }
}

@keyframes wave {
    0%,
    28.4%,
    56.8%,
    85.6% {
        transform: translateY(5px);
    }
    14.2%,
    42.6%,
    71%,
    100% {
        transform: translateY(-5px);
    }
}

В результате получилось то, чего я и добивался:

See the Pen Complex CSS animation is a lot of simple solutions-2 by haracan (@haracan) on CodePen.

Подобным образом можно оформить самую разнообразную анимацию, особенно такую, где необходимо, чтобы разная трансформация по X и Y применялась одновременно к одному и тому же объекту. То же относится и к развороту по оси. Например, иногда необходимо, чтобы во время воспроизведения анимации вдруг появлялась какая-то трансформация, которая уже используется нами и имеет свои значения, — в этом случае мы можем задать одну и ту же трансформацию основному объекту и его оболочке, назначив каждому из объектов отдельную анимацию и выставив разные значения одинаковой трансформации.

Проиллюстрирую это парашютистом, совершающем пируэты в небесах. Дам ему плавно разворачиваться по оси во время всего полёта вниз и одновременно заставлю его где-нибудь в середине пути совершить пируэт, развернувшись на 360 градусов. Заодно при помощи трансформации scale() создам эффект отдаления и приближения. Обратите внимание на то, что изначально у меня выставлено transform: scale(0.7), а не единица. Это сделано для того, чтобы объект при увеличении не терял чёткие контуры, а как бы возвращался в процессе трансформации к своему истинному размеру. Сначала в HTML создам простой код, наш парашютист будет состоять из трёх объектов, вложенных один в другой:

<div class="sky">
    <div class="pilot-wrapper">
        <div class="pilot">
            <img src="pilot.png">
        </div>
    </div>
</div>

В CSS задам стили каждому объекту, при этом к некоторым сразу же применю трансформацию, которая в дальнейшем будет использоваться в анимации, это необходимо, чтобы трансформация начиналась плавно и не деформировала объекты (как в случае со scale()):

body {
    margin: 0;
    padding: 0;
    text-align: center;
}

.sky {
    max-width: 500px;
    width: 100%;
    height: 100vh;
    background: linear-gradient(rgb(88, 85, 240), rgb(179, 208, 236));
    display: inline-block;
    overflow: hidden;
}

.pilot-wrapper {
    width: 200px;
    height: 100px;
    position: relative;
    left: 30%;
    transform: translateY(-110px);
}

.pilot {
    transform: scale(0.7);
}

.pilot img {
    width: 100%;
    height: 100%;
}

Теперь собственно сама анимация, которая будет начинаться при наведении на импровизированное небо и повторятся один раз:

.sky:hover>.pilot-wrapper>.pilot>img {
    animation: 2.5s ease-in 7s rotate;
}

.sky:hover>.pilot-wrapper>.pilot {
    animation: 17s ease-in-out 1 reverse wave-scale;
}

.sky:hover>.pilot-wrapper {
    animation: 15s ease-in-out 1 reverse to-bottom-rotate;
}

@keyframes rotate {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

@keyframes to-bottom-rotate {
    0% {
        transform: translateY(100vh) rotate(-45deg);
    }
    100% {
        transform: translateY(-110px) rotate(45deg);
    }
}

@keyframes wave-scale {
    0% {
        transform: translateX(100px);
    }
    20% {
        transform: translateX(-150px) scale(1);
    }
    40% {
        transform: translateX(100px);
    }
    60% {
        transform: translateX(-150px) scale(0.7);
    }
    80% {
        transform: translateX(100px);
    }
    100% {
        transform: translateX(-150px) scale(1);
    }
}

Итог трудов получился забавным:

See the Pen Complex CSS animation is a lot of simple solutions-3 by haracan (@haracan) on CodePen.


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

Дорогие друзья, если у вас есть какие-то вопросы и предложения, пишите. Искренне буду рад общению.

  • ⦁ создание сайтов ⦁
  • ⦁ индивидуальные проекты ⦁
  • ⦁ компьютерная графика ⦁