Анимация границ CSS

Несколько способов анимировать границу с помощью CSS

Настройка границ

Задать границу элемента можно несколькими способами: border, outline и box-shadow. Как подробно описано в статье Стефани Эклс Три метода CSS для добавления границ элементов, у каждого подхода есть свои преимущества и недостатки, особенно когда речь идет об анимации границ. Основная причина, по которой не стоит использовать правильный CSS border, – это анимация.

Анимация границ с помощью outline-offset, Кевин Дж. Powell

Недавно мне попалась статья Fantastic CSS border animation (Потрясающая анимация границ CSS), в которой автор Coco рассматривает дополнительные варианты. Добавляя сгенерированный контент с помощью ::before и ::after, они создают искусственную границу, которая затем анимируется.

Больше всего мне запомнились анимированные визуализации, которые использовались в статье. Они действительно помогают объяснить, что именно делается для достижения желаемого эффекта.

Анимация границ с использованием сгенерированного контента от Coco

Белый слой и цветные линии – это сгенерированный контент. Постепенно увеличивая и уменьшая прозрачность белого слоя, можно понять, как они накладываются друг на друга и как работает анимация.

Сохранение блочной модели

Недостаток использования сгенерированного контента для имитации границы заключается в том, что вы получаете сломанную блочную модель: контент теперь может скрывать искусственную границу, потому что указанная "граница" нарисована под ней. Чтобы устранить проблему, необходимо применить нужный border-width в качестве padding.

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

Основные сведения

Для начала создадим пунктирную границу и добавим несколько фонов.

/* Size of the border */
--border-size: 0.5rem;

/* Create a dotted border */
border: var(--border-size) dotted lime;

/* Create two background layers:
   1. A white semi-transparent
   2. A layer with the colored boxes
 */
background-image:
  linear-gradient(to right, rgb(255 255 255 / 0.5), rgb(255 255 255 / 0.5)),

  conic-gradient(
    from 45deg,
    #d53e33 0deg 90deg,
    #fbb300 90deg 180deg,
    #377af5 180deg 270deg,
    #399953 270deg 360deg
  )
;

Размеры фоновых изображений с background-origin

Как видите, с фоном происходит что-то странное: он закрашен в границах, но conic-gradient, похоже, совсем не то. Это ожидаемое поведение: по умолчанию фоновые изображения не заходят на границы, поскольку их начало координат – padding-box элемента. Чтобы создать границу, фоновые изображения повторяются в самой границе, что приводит к странному визуальному эффекту.

Чтобы решить эту проблему, нужно растянуть фон так, чтобы он занимал и размер границы. Вы можете сделать это вручную, растянув и переместив фон, но лучше использовать свойство background-origin, чтобы подогнать размер фона под размер border-box.

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 3.

Source

Не следует делать
/* Manually add or offset the size of the border where needed */
background-position: calc(var(--border-size) * -1) calc(var(--border-size) * -1);
background-size: calc(var(--border-size) * 2 + 100%) calc(var(--border-size) * 2 + 100%);
Что следует делать
background-origin: border-box;

Это одно дополнение делает все намного лучше:

Уменьшение белого фонового слоя с помощью background-clip

Поскольку фоновые изображения теперь занимают все пространство, полупрозрачный слой нужно снова уменьшить. Вместо того чтобы снова возиться с background-size, можно использовать более простой способ: задать для background-clip значение padding-box. В этом случае фон не будет отображаться под границей.

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 5.

Source

background-clip:
  padding-box, /* Clip white semi-transparent to the padding-box */
  border-box /* Clip colored boxes to the border-box (default) */
;

Наконец, сделайте границу transparent, чтобы получить полный эффект.

border: 0.3rem dotted transparent;

Анимация

Чтобы восстановить анимацию границы, можно изменить начальный угол conic-gradient.

--angle: 0deg;
conic-gradient(
  from var(--angle),
  #d53e33 0deg 90deg,
  #fbb300 90deg 180deg,
  #377af5 180deg 270deg,
  #399953 270deg 360deg
);

Благодаря @property это становится проще простого в браузерах, которые его поддерживают:

Browser Support

  • Chrome: 85.
  • Edge: 85.
  • Firefox: 128.
  • Safari: 16.4.

Source

@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

@keyframes rotate {
  to {
    --angle: 360deg;
  }
}

В результате код будет выглядеть следующим образом:

Бонусный контент: border-image

Ранее мы уже рассказывали, как нарисовать градиентную рамку с помощью свойства CSS border-image.

Browser Support

  • Chrome: 16.
  • Edge: 12.
  • Firefox: 15.
  • Safari: 6.

Source

Это позволяет упростить код, поскольку вам не нужно иметь дело с перекрывающимися фонами. Анимацию можно применять так же, как и раньше.

/* Create a border */
border: 0.5rem solid transparent;

/* Paint an image in the border */
border-image:
  conic-gradient(
    from var(--angle),
    #d53e33 0deg 90deg,
    #fbb300 90deg 180deg,
    #377af5 180deg 270deg,
    #399953 270deg 360deg
  ) 1
;

Однако при таком подходе некоторые функции перестанут работать:

  • border-image не повторяет border-radius и всегда остается прямоугольной.
  • Если для свойства border-image-slice задано значение fill, то border-image рисуется не под background, а поверх него. Это может быть неудобно, если вы хотите сделать фон полупрозрачным.

В заключение

В CSS есть множество возможностей для анимации границ. В зависимости от ситуации вы можете выбрать один из них.