CSS-магия: создаем идеальную «Ромашку» и радиальные меню на тригонометрии

📂 Категория: CSS
🏷️ Теги:

До недавнего времени верстка элементов по кругу была головной болью: либо мы использовали абсолютное позиционирование «на глаз», либо подключали громоздкие JS-скрипты для расчета top и left. С появлением нативной поддержки тригонометрических функций в CSS (Chrome 111+, Firefox 108+, Safari 15.4+), всё изменилось.

Как это работает? В основе лежит простая школьная математика. Чтобы расположить элемент на окружности, нам нужно знать угол (--angle) и радиус (--offset).

Формула проста:

  1. Мы определяем центр контейнера как точку (0,0).
  2. Считаем координату X как cos(угол) * радиус.
  3. Считаем координату Y как sin(угол) * радиус.
.petal {
  --_angle: calc(360deg * var(--_child) / var(--_total));
  --_x: calc(cos(var(--_angle)) * var(--_offset));
  --_y: calc(sin(var(--_angle)) * var(--_offset));

  transform: translate(-50%, -50%) translate(var(--_x), var(--_y));
}

Пример 1: Ромашка

Показать HTML код
<div class="daisy-container">
  <div class="petal" style="--_child: 0"></div>
  <div class="petal" style="--_child: 1"></div>
  <div class="petal" style="--_child: 2"></div>
  <div class="petal" style="--_child: 3"></div>
  <div class="petal" style="--_child: 4"></div>
  <div class="petal" style="--_child: 5"></div>
  <div class="petal" style="--_child: 6"></div>
  <div class="petal" style="--_child: 7"></div>
</div>
Показать CSS код
.daisy-container {
  --_offset: 8rem;
  --_size: 3rem;
  --_total: 8;
  position: relative;
  width: 4rem;
  aspect-ratio: 1;
  background: var(--color-primary);
  border-radius: 50%;
  margin-block: calc(var(--_offset) + 2rem);
  margin-inline: auto;
}

.petal {
  --_angle: calc(360deg * var(--_child) / var(--_total));
  --_x: calc(cos(var(--_angle)) * var(--_offset));
  --_y: calc(sin(var(--_angle)) * var(--_offset));
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--_size);
  aspect-ratio: 1;
  background: var(--color-primary-active);
  border-radius: 50%;
  transform: translate(-50%, -50%) translate(var(--_x), var(--_y));
}

Пример 2: Наши преимущества (6)

Наши
преимущества
🚀 Скорость
🛡️ Защита
💎 Качество
⚙️ Гибкость
🎧 Поддержка
📈 Рост
Показать HTML код
<div class="features-wrapper">
  <div class="features-circle">
    <div class="feature-hub">Наши<br />преимущества</div>

    <div class="feature" style="--_child: 0"><span>🚀</span> Скорость</div>
    <div class="feature" style="--_child: 1"><span>🛡️</span> Защита</div>
    <div class="feature" style="--_child: 2"><span>💎</span> Качество</div>
    <div class="feature" style="--_child: 3"><span>⚙️</span> Гибкость</div>
    <div class="feature" style="--_child: 4"><span>🎧</span> Поддержка</div>
    <div class="feature" style="--_child: 5"><span>📈</span> Рост</div>
  </div>
</div>
Показать CSS код
.features-wrapper {
  --_offset: 10rem;
  --_size: 8rem;
  --_total: 6;
  display: flex;
  justify-content: center;
  align-items: center;
  padding-block: calc(var(--_offset) + 3rem);
}

.features-circle {
  position: relative;
  width: 0;
  height: 0;
}

.feature-hub {
  position: absolute;
  width: 8rem;
  aspect-ratio: 1;
  background: #1e293b;
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 0.875rem;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.feature {
  --_angle: calc(360deg * var(--_child) / var(--_total));
  /* -90deg - чтобы первый элемент был сверху, а не справа */
  /* --_angle: calc(360deg * var(--_child) / var(--_total) - 90deg); */
  --_x: calc(cos(var(--_angle)) * var(--_offset));
  --_y: calc(sin(var(--_angle)) * var(--_offset));

  position: absolute;
  display: flex;
  flex-direction: column;
  width: var(--_size);
  padding: 1rem;
  background: white;
  border: 1px solid var(--color-base-borders);
  border-radius: 1rem;
  text-align: center;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  transform: translate(-50%, -50%) translate(var(--_x), var(--_y));
}

Пример 3: Наши преимущества (5)

Наши
преимущества
🚀 Скорость
🛡️ Защита
💎 Качество
⚙️ Гибкость
🎧 Поддержка
Показать HTML код
<div class="features-wrapper-2">
  <div class="features-circle-2">
    <div class="feature-hub-2">Наши<br />преимущества</div>

    <div class="feature-2" style="--_child: 0"><span>🚀</span> Скорость</div>
    <div class="feature-2" style="--_child: 1"><span>🛡️</span> Защита</div>
    <div class="feature-2" style="--_child: 2"><span>💎</span> Качество</div>
    <div class="feature-2" style="--_child: 3"><span>⚙️</span> Гибкость</div>
    <div class="feature-2" style="--_child: 4"><span>🎧</span> Поддержка</div>
  </div>
</div>
Показать CSS код
.features-wrapper-2 {
  --_offset: 10rem;
  --_size: 8rem;
  --_total: 5;
  display: flex;
  justify-content: center;
  align-items: center;
  padding-block: calc(var(--_offset) + 3rem);
}

.features-circle-2 {
  position: relative;
  width: 0;
  height: 0;
}

.feature-hub-2 {
  position: absolute;
  width: 8rem;
  aspect-ratio: 1;
  background: #1e293b;
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 0.875rem;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.feature-2 {
  /* --_angle: calc(360deg * var(--_child) / var(--_total)); */
  /* -90deg - чтобы первый элемент был сверху, а не справа */
  --_angle: calc(360deg * var(--_child) / var(--_total) - 90deg);
  --_x: calc(cos(var(--_angle)) * var(--_offset));
  --_y: calc(sin(var(--_angle)) * var(--_offset));

  position: absolute;
  display: flex;
  flex-direction: column;
  width: var(--_size);
  padding: 1rem;
  background: white;
  border: 1px solid var(--color-base-borders);
  border-radius: 1rem;
  text-align: center;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  transform: translate(-50%, -50%) translate(var(--_x), var(--_y));
}

Итоги: Математика на службе интерфейсов

Использование тригонометрии в CSS — это не просто эффектный трюк, а переход к более декларативному и чистому коду. Мы уходим от магических чисел и ручной подгонки пикселей к логике, которую понимает браузер.

Кроме того, использование sin и cos позволит вам получить персональное достижение: первое коммерческое применение этих малопригодных навыков из школы!

Похожие статьи