Хотите заказать веб-сайт? Связаться с нами

Функция shape() в CSS: создание сложных форм

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

Функция shape() в CSS: создание сложных форм

Почему появилась функция shape()

Долгое время веб-разработчики использовали для создания нестандартных форм свойство clip-path с базовыми функциями вроде 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-свойстве.

Доступны следующие варианты:

  1. проценты (%) — относительно ширины и высоты элемента;
  2. пиксели (px) — фиксированные значения;
  3. относительные единицы (em, rem) — для масштабирования относительно размера шрифта;
  4. вьюпортные единицы (vw, vh) — для привязки к размеру окна браузера;
  5. контейнерные единицы (cqw, cqh) — для компонентного подхода;
  6. математические функции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

  1. of rx ry — радиусы эллипса по горизонтали и вертикали;
  2. cw/ccw — направление (по или против часовой стрелки);
  3. large/small — выбирать большой или малый сегмент дуги;
  4. 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().

Доступны два варианта:

  1. nonzero (значение по умолчанию) — анализирует направление обхода каждого контура. Если внутренний контур обходится в сторону, противоположную внешнему, он создаёт вырезанную область. Если направления совпадают — внутренняя область остаётся залитой.
  2. 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 и позволяют точно позиционировать элементы кривой.

Доступны три точки привязки:

  1. from start — относительно начальной точки текущей команды;
  2. from end — относительно конечной точки текущей команды;
  3. 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() открывает широкие возможности, но теория без практики быстро забывается. Чтобы систематизировать знания и научиться создавать законченные проекты, приглашаем вас на онлайн-курс «Веб‑вёрстка с нуля до профессионала». Под руководством практикующих разработчиков вы соберёте портфолио и освоите востребованные навыки для старта карьеры.

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

Теги: