Функция shape() в CSS: создание сложных форм
Содержание
- Почему появилась функция shape()
- Основы работы с shape()
- Система координат и единицы измерения
- Команды для рисования линий
- Создание кривых и дуг
- Команда close и множественные контуры
- Относительные контрольные точки
- Анимация и переходы
- Практические примеры использования
- Отличия от path() и преимущества
- Поддержка браузерами и прогрессивное улучшение
- Создание адаптивных интерфейсных элементов
- Типичные ошибки и их решение
- Заключение
Сегодня при создании веб-сайтов требуются всё более изощрённые визуальные решения. Если раньше разработчики были ограничены прямоугольниками и кругами, то сегодня функция shape() в CSS позволяет создавать практически любые формы прямо в таблицах стилей. Это мощный инструмент, который пришёл на смену сложным SVG-путям и теперь доступен в большинстве современных браузеров.
Почему появилась функция shape()
circle(), ellipse() и polygon(). Этого хватало для простых геометрических фигур, но когда требовалось создать что-то более сложное — например, фигурный элемент с плавными изгибами или логотип нестандартной формы — приходилось прибегать к SVG.Функция path() частично решала эту проблему, позволяя вставлять SVG-пути прямо в CSS. Однако у неё был существенный недостаток: все координаты задавались только в пикселях. Это делало невозможным создание адаптивных форм, которые масштабируются вместе с родительским контейнером. Кроме того, синтаксис SVG-путей с их загадочными командами вроде M, L, C и Q оставался непонятным для многих разработчиков.
Функция shape() появилась именно для решения этих проблем. Она предлагает интуитивно понятный синтаксис на английском языке, поддержку любых единиц измерения в CSS измерения и возможность использовать математические функции.
Основы работы с shape()
Прежде чем погружаться в детали, важно понять структуру функции. Основы работы с shape() заключаются в правильном определении начальной точки и последовательности команд рисования.
Базовый синтаксис выглядит так:
.element {
clip-path: shape(from начальная-позиция, команда1, команда2, ...);
}
Первый параметр from задаёт начальную точку, от которой будет строиться вся фигура. После запятой идут команды, описывающие линии, кривые и дуги. Каждая команда отделяется запятой, что делает код лучше читаемым по сравнению с длинной строкой SVG-пути.
Пример 1
Создаем треугольник.
.triangleElement {
clip-path: shape(from 0% 100%, line to 50% 0%, line to 100% 100%, close);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
height: 200px;
width: 300px;
}
Этот код создаёт треугольник, направленный остриём вверх. Начальная точка находится в левом нижнем углу (0% 100%), затем линия ведёт к середине верхней грани (50% 0%), потом к правому нижнему углу (100% 100%), и команда close замыкает фигуру обратно к начальной точке.
Система координат и единицы измерения
shape() перед path() — поддержка всех современных CSS-единиц. Система координат и единицы измерения в shape() работают так же, как и в любом другом CSS-свойстве.Доступны следующие варианты:
- проценты (
%) — относительно ширины и высоты элемента; - пиксели (
px) — фиксированные значения; - относительные единицы (
em,rem) — для масштабирования относительно размера шрифта; - вьюпортные единицы (
vw,vh) — для привязки к размеру окна браузера; - контейнерные единицы (
cqw,cqh) — для компонентного подхода; - математические функции —
calc(),min(),max(),clamp().
Пример 2
Используем разные единицы в одной фигуре.
.adaptiveShape {
clip-path: shape(
from 20px 2rem,
line by calc(100% - 40px) 0,
curve to 80% 80% with 60% 20%,
vline by -30cqh,
close
);
width: 500px;
height: 400px;
background: coral;
}
Обратите внимание на использование calc() для вычисления положения линии, а также на контейнерную единицу cqh в вертикальной линии. Такой подход делает форму по-настоящему отзывчивой к изменениям размеров родителя.
Команды для рисования линий
shape() предлагает несколько вариантов для их создания. Команды для рисования линий включают как универсальные средства, так и специализированные для горизонтальных и вертикальных направлений.line — универсальная линия
Команда line рисует прямую линию от текущей точки до указанной координаты. Она принимает модификаторы to (абсолютные координаты) или by (координаты относительно предыдущей точки).
Пример 3
Пунктиром показан контейнер, из которого shape() вырезает заданную форму.
.lineExample {
clip-path: shape(
from 10px 10px,
line to 270px 10px, /* горизонтальная линия вправо */
line by 0 260px, /* вертикальная линия вниз */
close /* замыкаем форму */
);
background: teal;
height: 280px;
width: 280px;
}
hline и vline — специализированные линии
Для горизонтальных и вертикальных линий существуют отдельные команды: hline и vline, которые требуют указания только одной координаты. Это делает код чище и понятнее.
Пример 4
.hvlineDemo {
clip-path: shape(
from 0 40%,
hline to 30%,
vline to 20%,
hline to 90%,
vline to 60%,
hline to 100%,
vline to 80%,
hline to 0,
close
);
background: slateblue;
height: 200px;
width: 280px;
}
Команда hline изменяет только x-координату, оставляя y неизменной. vline, соответственно, меняет только y. Модификаторы by и to работают так же, как и в обычной line.
Собери свой код. Запусти сайт!
От наброска на салфетке до первого работающего лендинга. Наш онлайн-курс «Веб-верстка с нуля и до профессионала» — это интенсивный трек, где ты не будешь зубрить теорию, а с первого дня начнешь превращать идеи в чистый HTML и CSS.
Собери свой первый проект под руководством практикующих разработчиков.
Создание кривых и дуг
shape() раскрывается при работе с кривыми. Создание кривых и дуг позволяет добиться плавных переходов и органичных форм, недоступных для простых многоугольников.curve — кривые Безье
Команда curve создаёт кривую Безье. В зависимости от количества контрольных точек получается квадратичная (одна точка) или кубическая (две точки) кривая.
Пример 5
.curveShape {
clip-path: shape(
from 10% 50%,
curve to 90% 50% with 30% 10% / 70% 90%,
/* кубическая кривая с двумя контрольными точками */
line to 10% 50%,
close
);
background: mediumpurple;
height: 280px;
width: 280px;
}
Синтаксис curve to endpoint with control-point1 / control-point2 создаёт плавный изгиб. Если указана только одна контрольная точка, получается квадратичная кривая, которая всегда проходит через начальную, конечную и контрольную точки.
smooth — сглаженные кривые
Команда smooth создаёт кривую, которая продолжает предыдущую с сохранением плавности. Сглаженные кривые особенно полезны при создании сложных контуров, где важна непрерывность изгиба.
Пример 6
.smoothDemo {
clip-path: shape(
from 14px 140px,
curve to 142px 14px with 42px 14px,
smooth to 256px 140px,
smooth to 140px 266px with 124px -2px,
close
);
background: lightseagreen;
height: 280px;
width: 280px;
}
Когда smooth используется без контрольной точки, она автоматически рассчитывает отражение последней контрольной точки предыдущей кривой. Это гарантирует отсутствие изломов в месте соединения.
arc — эллиптические дуги
Команда arc рисует часть эллипса. Это наиболее сложная для понимания, но незаменимая команда для создания закруглений и органичных форм. Эллиптические дуги требуют указания размеров эллипса и параметров отрисовки.
Пример 7
.arcShape {
clip-path: shape(
from 30px 170px,
arc to 250px 170px of 131px 50px ccw large rotate 0deg,
line to 250px 70px,
arc to 30px 70px of 110px 50px cw large rotate 0deg,
close
);
background: darkorange;
height: 280px;
width: 280px;
}
Параметры arc
of rx ry— радиусы эллипса по горизонтали и вертикали;cw/ccw— направление (по или против часовой стрелки);large/small— выбирать большой или малый сегмент дуги;rotate angle— поворот эллипса.
Если радиусы равны, дуга становится круговой. При отсутствии второго радиуса он считается равным первому.
Команда close и множественные контуры
close и множественные контуры позволяют создавать сложные составные фигуры с отверстиями или дополнительными элементами.Команда close без параметров просто соединяет текущую точку с начальной точкой всей фигуры. Но если после close идёт команда move, начинается новый подконтур.
Пример 7
.compositeShape {
clip-path: shape(
from 10% 30%,
line to 90% 10%,
line to 90% 70%,
line to 10% 90%,
close,
move to 50% 30%,
line to 30% 70%,
line to 50% 70%,
line to 70% 30%,
close
);
background: indianred;
height: 280px;
width: 280px;
}
При создании сложных фигур с помощью shape() нередко возникает необходимость сделать внутреннее отверстие — например, кольцо или рамку. В таких случаях используется техника множественных контуров: первый контур задаёт внешнюю границу, а последующие описывают области, которые должны быть вырезаны.
Однако просто добавить второй контур недостаточно — нужно понимать, как браузер определяет, какие области считать внутренними, а какие вырезанными. За это отвечает правило заполнения (fill-rule), которое задаётся первым параметром функции shape().
Доступны два варианта:
nonzero(значение по умолчанию) — анализирует направление обхода каждого контура. Если внутренний контур обходится в сторону, противоположную внешнему, он создаёт вырезанную область. Если направления совпадают — внутренняя область остаётся залитой.evenodd— более простой и предсказуемый вариант. Он не обращает внимания на направление обхода: любой вложенный контур автоматически считается отверстием, независимо от того, по часовой стрелке он нарисован или против.
Выбор правила зависит от задачи. Если вам нужно гарантированно получить вырезанную область и не хочется следить за направлением обхода контуров — используйте evenodd. Если же требуется более тонкое управление (например, создать сложную фигуру с перекрытиями, где не все внутренние области должны быть отверстиями), пригодится nonzero с контролем направления обхода.
Пример 8
Используем правило evenodd.
.exampleEvenodd {
clip-path: shape(
evenodd from 10% 10%,
line to 90% 10%,
line to 90% 90%,
line to 10% 90%,
close,
move to 30% 30%,
line to 70% 30%,
line to 70% 70%,
line to 30% 70%,
close /* направление не важно = всегда вырезание */
);
}
Относительные контрольные точки
shape() — привязка контрольных точек к разным системам координат. Относительные контрольные точки задаются с помощью ключевого слова from и позволяют точно позиционировать элементы кривой.Доступны три точки привязки:
from start— относительно начальной точки текущей команды;from end— относительно конечной точки текущей команды;from origin— относительно верхнего левого угла элемента.
Пример 9
.relativeControls {
clip-path: shape(
from 0 0,
curve by 250px 200px with 50px 86px from start / 150px 160px from origin,
smooth by -200px 0 with 50px 150px from end,
close
);
background: mediumseagreen;
height: 280px;
width: 280px;
}
Анимация и переходы
Хотя текущая реализация shape() имеет ограничения по анимации, некоторые эффекты всё же достижимы.
Анимация и переходы с shape() возможны при изменении формы между ключевыми кадрами, если количество команд остаётся одинаковым.
Пример 10
@keyframes morphShape {
0% {
clip-path: shape(from 0% 0%, line to 100% 0%, line to 50% 100%, close);
}
50% {
clip-path: shape(from 0% 0%, line to 100% 50%, line to 0% 100%, close);
}
100% {
clip-path: shape(from 0% 0%, line to 100% 0%, line to 50% 100%, close);
}
}
.animatedElement {
width: 400px;
height: 400px;
background: linear-gradient(45deg, #f093fb 0%, #f5576c 100%);
animation: morphShape 4s infinite ease-in-out;
}
Важно помнить: анимировать можно только одинаковое количество команд с одинаковыми типами. Нельзя плавно превратить линию в кривую. Однако для однотипных команд браузер выполняет интерполяцию координат, создавая плавные переходы.
Практические примеры использования
shape() в реальных проектах.Форма капли для логотипа
Пример 11
.dropShape {
width: 180px;
aspect-ratio: 0.737;
background-color: lightblue;
clip-path: shape(
from 50% 100%,
arc by 50% -36.84% of 50% 36.84% small ccw,
curve by -21.43% -28.95% with 0% -10.53%/-7.14% -20.53%,
smooth by -28.57% -34.21% with -25% -21.05%,
curve by -28.57% 34.21% with -3.57% 13.16%/-14.29% 25.79%,
curve to 0% 63.16% with 7.14% 42.63%/0% 52.63%,
arc by 50% 36.84% of 50% 36.84% small ccw,
close);
}
Отличия от path() и преимущества
Многие разработчики знакомы с функцией path(), поэтому важно понимать отличия от path() и преимущества нового подхода.
Таблица основных отличий
| Критерий | path() |
shape() |
|---|---|---|
| Синтаксис | строка с SVG-синтаксисом (M, L, C, Q и т.д.) |
структурированные команды на английском языке (line, curve, arc) |
| Единицы измерения | только пиксели (px) |
любые CSS-единицы (%, em, rem, vw, vh, cqh и др.) |
| Читаемость | сложно читать и модифицировать | интуитивно понятный, лёгкий для редактирования |
| Математические функции | не поддерживает calc() |
полноценная поддержка calc(), min(), max(), clamp() |
| Порог вхождения | требует знания SVG-команд | использует обычные английские слова, понятные без специального обучения |
Пример 12
Сравнение одного и того же треугольника.
/* Старый способ с path() */
.oldWay {
clip-path: path("M 0 100 L 50 0 L 100 100 Z");
}
/* Новый способ с shape() */
.newWay {
clip-path: shape(from 0% 100%, line to 50% 0%, line to 100% 100%, close);
}
Разница особенно заметна при создании адаптивных форм. В shape() можно написать line to 100% 50%, и линия всегда будет заканчиваться посередине правого края независимо от размеров элемента. В path() пришлось бы вычислять пиксели и переписывать код при каждом изменении размеров.
Поддержка браузерами и прогрессивное улучшение
На момент написания этой статьи функция shape() поддерживается всеми основными браузерными движками, что делает её использование в продакшене гораздо более безопасным, хотя всё ещё рекомендуется применять прогрессивное улучшение с запасными вариантами в виде polygon() для обеспечения работы в более старых версиях браузеров.
Для проектов, где важна обратная совместимость, рекомендуется использовать прогрессивное улучшение:
Пример 13
/* Базовый стиль для всех браузеров */
.modernShape {
background: #3498db;
border-radius: 10px;
}
/* Улучшение для поддерживающих браузеров */
@supports (clip-path: shape(from 0 0, line to 100% 100%)) {
.modernShape {
clip-path: shape(from 0% 0%, line to 100% 0%, line to 80% 100%, close);
border-radius: 0;
}
}
Такой подход гарантирует, что в старых браузерах элемент будет выглядеть приемлемо (пусть и менее эффектно), а в современных — использовать все возможности shape().
Создание адаптивных интерфейсных элементов
Одно из главных преимуществ shape() — возможность создавать по-настоящему адаптивные интерфейсные элементы, которые масштабируются вместе с контентом.
Рассмотрим создание фигурной кнопки с адаптивными пропорциями:
Пример 14
Создание фигурной кнопки с адаптивными пропорциями.
.responsiveButton {
display: grid;
place-items: center;
aspect-ratio: 1;
width: 280px;
height: 280px;
clip-path: shape(
from 41.18% 31.55%,
arc by 5.88% -6.15% of 5.88% 6.15% small ccw,
vline to 7.33%,
arc by 10.65% -4.61% of 5.88% 6.15% small cw,
line by 40.21% 42.04%,
arc by 0% 10.5% of 7.1% 7.42% small cw,
line by -40.21% 42.03%,
arc by -10.65% -4.61% of 5.88% 6.15% small cw,
vline to 74.6%,
arc by -5.88% -6.15% of 5.88% 6.15% small ccw,
hline to 5.88%,
arc by -5.88% -6.15% of 5.88% 6.15% small cw,
vline by -24.6%,
arc by 5.88% -6.15% of 5.88% 6.15% small cw,
close);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 2.1rem;
transition: transform 0.2s;
will-change: transform;
}
.responsiveButton:hover {
transform: scale(1.05);
}
Типичные ошибки и их решение
При работе с новой функцией неизбежны ошибки. Рассмотрим типичные ошибки и их решение.
Забытая запятая
/* Ошибка */
.wrong {
clip-path: shape(from 0 0 line to 100px 100px close);
}
/* Правильно */
.correct {
clip-path: shape(from 0 0, line to 100px 100px, close);
}
Неправильный порядок координат
/* Ошибка - перепутаны x и y */
.wrong {
clip-path: shape(from 20px 10px, vline to 30px); /* vline ожидает y, а получает x */
}
/* Правильно */
.correct {
clip-path: shape(from 20px 10px, vline to 30px); /* если нужно изменить y */
/* или */
clip-path: shape(from 20px 10px, hline to 30px); /* если нужно изменить x */
}
Несовместимые типы команд при анимации
/* Ошибка - нельзя анимировать */
@keyframes wrongAnimation {
0% { clip-path: shape(from 0 0, line to 100px 100px, close); }
100% { clip-path: shape(from 0 0, curve to 100px 100px with 50px 50px, close); }
}
/* Правильно - одинаковые типы команд */
@keyframes correctAnimation {
0% { clip-path: shape(from 0 0, curve to 100px 100px with 20px 20px, close); }
100% { clip-path: shape(from 0 0, curve to 100px 100px with 80px 80px, close); }
}
Заключение
Освоение современных CSS-функций вроде shape() открывает широкие возможности, но теория без практики быстро забывается. Чтобы систематизировать знания и научиться создавать законченные проекты, приглашаем вас на онлайн-курс «Веб‑вёрстка с нуля до профессионала». Под руководством практикующих разработчиков вы соберёте портфолио и освоите востребованные навыки для старта карьеры.
Если же вам нужен готовый сайт под ключ — наша веб‑студия разработает проект любой сложности с учётом всех современных требований. Обращайтесь для обсуждения или записывайтесь на курс, чтобы создавать такие проекты самостоятельно.