Содержание

Меняем форму, масштаб и положение объектов, выстраивая яркие эффекты
15 минут чтения
Статичные модули
Динамичные модули
Содержание
В модулях «Движение» и «Вращение» мы работали с отдельными функциями transform, translate перемещал объекты, rotate крутили их. Здесь мы разбираем оставшуюся палитру: scale, skew, clip-path, perspective и учились комбинировать их друг с другом. Модуль устроен компактнее предыдущих. Чуть меньше объяснений, чуть больше кода и примеров.
Объект увеличивается или уменьшается. scale(1) исходный размер, scale(1.5) в полтора раза больше, scale(0) — невидимая точка. В отличие от изменения width/ height, масштабирование через scale не сдвигает соседние элементы и не пересчитывает раскладку — объект трансформируется на месте, в своей визуальной плоскости.
Анимированный scale хорош для появления элементов. scale(0) → scale(1) — объект вырастает из точки. Добавьте opacity:0 → 1 для мягкости. Обратный вариант c scale(1) → scale(0) схлопывает объект в точку.
Обратите внимание: scale масштабирует всё содержимое элемента, включая текст, обводку и тени. Если вам нужно масштабировать только контейнер, а текст оставить читаемым, придётся компенсировать scale на дочерних элементах.
Попробуйте поставить рядом три фигуры: круг, квадрат и треугольник с одной и той же анимацией вращения. Это хорошее упражнение, чтобы почувствовать, как геометрия формы меняет визуальное восприятие одного и того же технического приёма.
<div class="demo-scale-circle"></div>
.demo-scale-circle {
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
border-radius: 50%;
background: #fff;
animation: transform-scale 2.5s ease-in-out infinite;
}
@keyframes transform-scale {
0% { transform: scale(0); opacity: 0; }
50% { transform: scale(1); opacity: 1; }
100% { transform: scale(0); opacity: 0; }
}
scaleX и scaleY работают по отдельности и это открывает территорию деформаций. Объект сжимается по горизонтали и вытягивается по вертикали или наоборот.
Приём особенно выразителен для абстрактных фигур: круг, который ритмично превращается в вертикальный овал, а потом в горизонтальный. Или полоски эквалайзера — ряд элементов, каждый из которых пульсирует по scaleY с разной амплитудой и задержкой.
<div class="demo-stretch-bar"></div>
<div class="demo-stretch-bar"></div>
<div class="demo-stretch-bar"></div>
<div class="demo-stretch-bar"></div>
<div class="demo-stretch-bar"></div>
<div class="demo-stretch-bar"></div>
<div class="demo-stretch-bar"></div>
.demo-stretch-bar {
position: absolute;
bottom: 30%;
width: 30px;
height: 20px;
background: #fff;
transform-origin: bottom center;
animation: transform-stretch 1s ease-in-out infinite alternate;
}
/* Индивидуальные длительности для каждой полоски */
.demo-stretch-bar:nth-child(1) { left: 15%; animation-duration: 0.8s; }
.demo-stretch-bar:nth-child(2) { left: 25%; animation-duration: 1.1s; }
.demo-stretch-bar:nth-child(3) { left: 35%; animation-duration: 0.7s; }
.demo-stretch-bar:nth-child(4) { left: 45%; animation-duration: 1.3s; }
.demo-stretch-bar:nth-child(5) { left: 55%; animation-duration: 0.9s; }
.demo-stretch-bar:nth-child(6) { left: 65%; animation-duration: 1.2s; }
.demo-stretch-bar:nth-child(7) { left: 75%; animation-duration: 0.6s; }
@keyframes transform-stretch {
0% { transform: scaleY(1); }
100% { transform: scaleY(6); }
}
Если вы применяете scaleX или scaleY к элементу с текстом, буквы тоже тянутся или сжимаются. Иногда это нужный дизайнерский эффект, но чаще текст становится нечитаемым. Если хотите деформировать контейнер, не трогая текст, компенсируйте трансформацию на дочернем элементе: scaleY(2) на родителе, scaleY(0.5) на тексте.
skewX и skewY наклоняют элемент, превращая прямоугольник в параллелограмм. Визуально это ощущение скорости, напора, курсивности. В типографических плакатах skew создаёт летящие текстовые блоки — заголовок под углом, словно сдвинутый порывом ветра.
Анимированный skew с небольшой амплитудой (5–15°) даёт деформацию другого характера, чем дрожь через rotate. Если rotate трясёт объект, то skew его мнёт, сдвигая горизонтальные линии относительно друг друга. Попробуйте оба ффекта на одном элементе и практически сразу разница в ощущениях станет заметна.
skewX наклоняет по горизонтали: верхняя и нижняя грани смещаются в разные стороны. skewY — по вертикали: левая и правая грани. Комбинация обоих даёт сложную ромбовидную деформацию.
<div class="demo-skew-block"></div>
.demo-skew-block {
position: absolute;
top: 35%;
left: 50%;
width: 180px;
height: 80px;
margin-left: -90px;
background: #fff;
animation: transform-skew 3s ease-in-out infinite alternate;
}
@keyframes transform-skew {
0% { transform: skewX(-15deg); }
100% { transform: skewX(15deg); }
}
Хорошим решением для декоративных полос в плакате сделать несколько горизонтальных полосок с одинаковым skewX, расставленных вертикально. Получается динамичный паттерн наклонных параллелограммов, который делается тремя строчками CSS.
scaleX(-1) зеркалит элемент по горизонтали. scaleY(-1) — по вертикали. Мгновенный переворот, без промежуточных состояний. Казалось бы, простой трюк — но в комбинации с анимацией и дублированием он открывает интересные возможности.
Два одинаковых объекта рядом, один из которых отражён — эффект симметрии, чернильного пятна Роршаха, отражения в воде. Анимированное отражение через scaleX(1) и scaleX(-1) — быстрый флип, визуально похожий на 3D-переворот, но без perspective. Для текстовых элементов это создаёт эффект обратного чтения, зеркальной надписи.
<div class="demo-flip-axis"></div>
<div class="demo-flip-original"></div>
<div class="demo-flip-mirror"></div>
.demo-flip-original {
position: absolute;
top: 30%;
left: 25%;
width: 60px;
height: 100px;
background: #fff;
border-radius: 4px 30px 4px 4px;
animation: flip-pulse-right 3s ease-in-out infinite;
}
.demo-flip-mirror {
position: absolute;
top: 30%;
left: calc(75% - 60px);
width: 60px;
height: 100px;
background: rgba(255, 255, 255, 0.4);
border-radius: 4px 30px 4px 4px;
/* Зеркальное отражение */
transform: scaleX(-1);
animation: flip-pulse-left 3s ease-in-out infinite;
}
.demo-flip-axis {
position: absolute;
top: 20%; left: 50%;
width: 1px; height: 60%;
background: rgba(255, 255, 255, 0.15);
}
@keyframes flip-pulse-right {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(30px); }
}
@keyframes flip-pulse-left {
0%, 100% { transform: scaleX(-1) translateX(0); }
50% { transform: scaleX(-1) translateX(30px); }
}
Принцип из классической анимации Диснея, когда объект при движении деформируется. Мяч, падающий на поверхность, в момент удара сплющивается по вертикали и расширяется по горизонтали. При отскоке происходит наоборот, вытягивается вверх и сужается. Это добавляет движению вес и характер, превращает механическое перемещение в живое, мультяшное и живое.
Технически это комбинация translateY с scaleX / scaleY в одном многошаговом @keyframes. В момент приземления — scaleY(0.6) scaleX(1.3), в момент взлёта — scaleY(1.2) scaleX(0.85).
<div class="demo-ball-shadow"></div>
<div class="demo-ball"></div>
.demo-ball {
position: absolute;
left: 50%;
bottom: 20%;
width: 60px;
height: 60px;
border-radius: 50%;
background: #fff;
/* Деформация от нижней грани */
transform-origin: bottom center;
animation: squash-stretch 1s ease infinite;
}
@keyframes squash-stretch {
0% { transform: translateY(0) scaleX(1.3) scaleY(0.6); }
15% { transform: translateY(-20px) scaleX(0.9) scaleY(1.15); }
40% { transform: translateY(-120px) scaleX(1) scaleY(1); }
65% { transform: translateY(-20px) scaleX(0.9) scaleY(1.15); }
85% { transform: translateY(0) scaleX(1.3) scaleY(0.6); }
100% { transform: translateY(0) scaleX(1) scaleY(1); }
}
/* Тень, реагирующая на высоту мяча */
.demo-ball-shadow {
position: absolute;
left: 50%;
bottom: 18%;
width: 60px;
height: 10px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.15);
animation: squash-shadow 1s ease infinite;
}
@keyframes squash-shadow {
0% { transform: scaleX(1.2); opacity: 0.3; }
40% { transform: scaleX(0.5); opacity: 0.08; }
85% { transform: scaleX(1.2); opacity: 0.3; }
100% { transform: scaleX(1); opacity: 0.25; }
}
Важно transform-origin: bottom center, чтобы объект деформировался от точки контакта, а не от своего центра, иначе он будет зависать в воздухе при сплющивании.
Приём создаёт ощущение глубины у плоских элементов. Текстовый блок с perspective: 800px и rotateX(5deg) выглядит так, словно он чуть отклонён от зрителя. Изображение с rotateY(10deg) будто стоит под углом, как рамка на полке. Такая плавная анимация с лёгким покачиванием в перспективе делает карточку или блок объёмным и живым.
Значение perspective на родителе определяет расстояние камеры. Маленькое значение в (200–400px) — агрессивная перспектива, грани сильно уходят вдаль. (800–1200px) дают мягкую, едва заметную глубину.
<div class="demo-tilt-scene">
<div class="demo-tilt-card"></div>
</div>
/* Контейнер задаёт перспективу */
.demo-tilt-scene {
perspective: 600px;
}
.demo-tilt-card {
width: 160px;
height: 200px;
background: #fff;
border-radius: 8px;
animation: perspective-tilt 6s ease-in-out infinite;
}
@keyframes perspective-tilt {
0% { transform: rotateY(0deg) rotateX(0deg); }
25% { transform: rotateY(12deg) rotateX(-5deg); }
50% { transform: rotateY(0deg) rotateX(0deg); }
75% { transform: rotateY(-12deg) rotateX(5deg); }
100% { transform: rotateY(0deg) rotateX(0deg); }
}
clip-path: polygon() позволяет вырезать из элемента произвольную фигуру, которая анимируется. Квадрат плавно перетекает в звезду, звезда в круг,а круг в ромб. Выглядит так, будто форма живая, будто она дышит и меняется.
Технически всё просто: вы задаёте несколько ключевых кадров с разными polygon(), а браузер интерполирует точки между ними. Есть одно жёсткое правило: количество точек в каждом полигоне должно совпадать. Если в первом кадре 6 точек, а во втором 8 — браузер не сможет интерполировать и просто перепрыгнет к следующей форме без анимации.
<div class="demo-morph-shape"></div>
.demo-morph-shape {
width: 160px;
height: 160px;
background: #fff;
animation: transform-morph 6s ease-in-out infinite;
}
@keyframes transform-morph {
0% {
/* Скруглённый квадрат (8 точек) */
clip-path: polygon(
30% 0%, 70% 0%, 100% 30%, 100% 70%,
70% 100%, 30% 100%, 0% 70%, 0% 30%
);
}
25% {
/* Звезда */
clip-path: polygon(
50% 0%, 65% 35%, 100% 35%, 75% 55%,
85% 100%, 50% 70%, 15% 100%, 25% 55%
);
}
50% {
/* Круг-аппроксимация */
clip-path: polygon(
50% 0%, 85% 15%, 100% 50%, 85% 85%,
50% 100%, 15% 85%, 0% 50%, 15% 15%
);
}
75% {
/* Ромб */
clip-path: polygon(
50% 5%, 80% 25%, 95% 50%, 80% 75%,
50% 95%, 20% 75%, 5% 50%, 20% 25%
);
}
100% {
clip-path: polygon(
30% 0%, 70% 0%, 100% 30%, 100% 70%,
70% 100%, 30% 100%, 0% 70%, 0% 30%
);
}
}
/* Все полигоны должны иметь одинаковое количество точек */
Порядок точек в полигоне влияет на траекторию морфинга. Первая точка первой формы интерполируется в первую точку второй формы, вторая во вторую, и так далее. Если порядок сбит, форма при переходе будет скручиваться в непредсказуемый клубок. Иногда это именно тот эффект, который нужен, но чаще стоит следить за последовательностью.
Эффект, когда элемент разрывается на горизонтальные полосы, которые сдвигаются в разные стороны и произвольно наклоняются в разные стороны. Классический глитч-арт, адаптированный под технологии CSS.
Техника строится на псевдоэлементах ::before и ::after. Основной элемент, его ::before и ::after — три слоя одного и того же содержимого. Каждый слой получает свой clip-path, свой translate и свой цветовой сдвиг. Анимация быстрая и рваная с animation-timing-function: steps(1) вместо плавного тайминга.
<div class="demo-glitch-block"></div>
<!-- ::before и ::after создаются через CSS -->
.demo-glitch-block {
width: 180px;
height: 80px;
background: #fff;
position: relative;
}
.demo-glitch-block::before,
.demo-glitch-block::after {
content: '';
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: inherit;
}
.demo-glitch-block::before {
animation: glitch-top 3s steps(1) infinite;
}
.demo-glitch-block::after {
animation: glitch-bottom 3s steps(1) infinite;
}
@keyframes glitch-top {
0%, 24%, 63%, 100% {
clip-path: inset(0 0 60% 0);
transform: translate(0) skewX(0);
opacity: 0;
}
20% {
clip-path: inset(0 0 60% 0);
transform: translate(-8px, -2px) skewX(-4deg);
opacity: 1;
}
62% {
clip-path: inset(0 0 50% 0);
transform: translate(10px, -1px) skewX(5deg);
opacity: 1;
}
}
Это концептуальный паттерн, который объясняет почему translate(100px) rotate(45deg) и rotate(45deg) translate(100px) дают разный результат. Каждая функция в transform работает в системе координат, которую изменила предыдущая.
Представьте, что вы стоите лицом к стене. Сначала шаг вправо, потом поворот, вы шагнули вправо и повернулись на месте. Сначала поворот, потом шаг вправо и вы повернулись, теперь вправо для вас это уже другое направление, потому что ваша система координат повернулась вместе с вами.
В демо ниже два одинаковых объекта получают одинаковый набор трансформаций, но в разном порядке. Разница в конечной позиции — наглядное доказательство того, что порядок имеет значение.
<div class="demo-order-origin"></div>
<!-- A: translate → rotate -->
<div class="demo-order-a"></div>
<!-- B: rotate → translate -->
<div class="demo-order-b"></div>
/* Объект A: сначала translate, потом rotate */
.demo-order-a {
width: 50px;
height: 50px;
background: #fff;
animation: order-tr 4s ease-in-out infinite alternate;
}
/* Объект B: сначала rotate, потом translate */
.demo-order-b {
width: 50px;
height: 50px;
border: 2px dashed rgba(255,255,255,0.6);
background: rgba(255,255,255,0.4);
animation: order-rt 4s ease-in-out infinite alternate;
}
@keyframes order-tr {
0% { transform: translate(0,0) rotate(0deg); }
100% { transform: translate(120px,0) rotate(90deg); }
}
@keyframes order-rt {
0% { transform: rotate(0deg) translate(0,0); }
100% { transform: rotate(90deg) translate(120px,0); }
}
Несколько плоских элементов, расположенных в 3D-пространстве через translateZ и rotateY, формируют объёмную конструкцию веера. По сути это развитие цилиндрического текста из модуля «Вращение», но с произвольной геометрией и более свободной раскладкой.
Каждый элемент смещён по оси Z на разное расстояние и повёрнут на свой угол. С perspective на родителе ближние элементы выглядят крупнее, дальние наоборот меньше. Медленное вращение всей конструкции через rotateY на контейнере и вы получаете карусель, которая демонстрирует все элементы по очереди.
В отличие от цилиндра, где все элементы на одном радиусе, здесь можно варьировать translateZ для каждого. Кто-то ближе, кто-то дальше. Это создаёт ощущение глубины и пространственной сложности.
<div class="demo-spread-scene">
<div class="demo-spread-wrap">
<div class="demo-spread-card"></div>
<div class="demo-spread-card"></div>
<div class="demo-spread-card"></div>
<div class="demo-spread-card"></div>
<div class="demo-spread-card"></div>
<div class="demo-spread-card"></div>
</div>
</div>
.demo-spread-scene {
perspective: 800px;
transform-style: preserve-3d;
}
.demo-spread-wrap {
transform-style: preserve-3d;
animation: spread-spin 16s linear infinite;
}
.demo-spread-card {
width: 80px;
height: 110px;
background: #fff;
border-radius: 6px;
backface-visibility: hidden;
}
/* 6 карточек по кругу, шаг 60° */
.demo-spread-card:nth-child(1) { transform: rotateY(0deg) translateZ(140px); }
.demo-spread-card:nth-child(2) { transform: rotateY(60deg) translateZ(140px); }
.demo-spread-card:nth-child(3) { transform: rotateY(120deg) translateZ(140px); }
.demo-spread-card:nth-child(4) { transform: rotateY(180deg) translateZ(140px); }
.demo-spread-card:nth-child(5) { transform: rotateY(240deg) translateZ(140px); }
.demo-spread-card:nth-child(6) { transform: rotateY(300deg) translateZ(140px); }
@keyframes spread-spin {
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
Группа объектов, изначально собранных в одной точке или в компактной форме, разлетается в разные стороны. Каждый элемент получает свой вектор через translate, свой угол через rotate, своё масштабирование через scale. Обратный эффект, когда элементы собираются из хаоса в цельную форму работает не менее выразительно.
Для сборки достаточно инвертировать анимацию: начать с разлетевшихся позиций и привести всё к центру. animation-fill-mode: forwards зафиксирует конечное состояние.
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
<div class="demo-explode-piece"></div>
.demo-explode-piece {
position: absolute;
top: 50%; left: 50%;
width: 30px;
height: 30px;
background: #fff;
animation: 2.5s ease-out infinite;
}
/* Каждый фрагмент летит в свою сторону */
.demo-explode-piece:nth-child(1) { animation-name: explode-1; }
.demo-explode-piece:nth-child(2) { animation-name: explode-2; }
/* ...и так далее для каждого */
@keyframes explode-1 {
0% { transform: translate(0,0) rotate(0deg) scale(1); opacity: 1; }
100% { transform: translate(140px,-100px) rotate(180deg) scale(0.3); opacity: 0; }
}
@keyframes explode-2 {
0% { transform: translate(0,0) rotate(0deg) scale(1); opacity: 1; }
100% { transform: translate(-120px,-80px) rotate(-150deg) scale(0.2); opacity: 0; }
}
При разлёте объекты могут выйти за пределы контейнера с overflow: hidden и просто обрежутся. Если вам нужно, чтобы объекты улетали за экран, то убедитесь, что у контейнера достаточно пространства, или уберите overflow: hidden
Несколько пересекающихся геометрических фигур, каждая из которых медленно трансформируется по-своему: одна слегка увеличивается через scale, другая сужается по X через scaleX, третья наклоняется через skew, четвёртая чуть вращается. Разные длительности гарантируют, что паттерн повторения не считывается.
Паттерн хорошо работает как фоновый слой. Декоративные формы за основным содержанием плаката, которые создают ощущение глубины и движения без отвлечения внимания.
<div class="demo-breath-shape"></div>
<div class="demo-breath-shape"></div>
<div class="demo-breath-shape"></div>
<div class="demo-breath-shape"></div>
.demo-breath-shape {
position: absolute;
border-radius: 30% 70% 50% 50%;
background: rgba(255, 255, 255, 0.15);
}
/* Каждая фигура — своя форма, размер и анимация */
.demo-breath-shape:nth-child(1) {
width: 200px; height: 180px;
animation: breath-1 11s ease-in-out infinite;
}
.demo-breath-shape:nth-child(2) {
width: 180px; height: 160px;
border-radius: 50% 30% 70% 40%;
animation: breath-2 7s ease-in-out infinite;
}
@keyframes breath-1 {
0%, 100% { transform: scale(1) skew(0deg); }
50% { transform: scale(1.08) skew(3deg); }
}
@keyframes breath-2 {
0%, 100% { transform: scale(1) rotate(0deg); }
50% { transform: scale(0.92) rotate(8deg); }
}
Для по-настоящему органичного дыхания используйте на каждой фигуре несколько вложенных обёрток с разными трансформациями. Одна обёртка отвечает за scale, другая — за skew, третья — за rotate. Каждая со своей длительностью.