Анимация CSS достигает эффекта вращения планеты

внешний интерфейс CSS
Анимация CSS достигает эффекта вращения планеты

Всем привет, я первокурсник 1997 года, маленький свежий мясной зеленый школьник, и каждый день встречаю "странные" идеи дизайнера🤔. Сегодня я предлагаю вам практический совет по фронтенду. Работая над страницей активности H5, дизайнер Ямада придумал визуализацию планетарного движения по орбите 🪐, пять шаров должны вращаться по наклонной орбите. JavaScript может рисовать множество сложных анимаций, и в Интернете есть много реализаций различных классов планет, так как же использовать CSS для достижения такого эффекта?

Эффект реализации CSS:

1616045664764-f8401335-1219-43c2-96e5-35fab4e8011f.gif

Ссылка на онлайн-демонстрацию

Понимание преобразования CSS

Прежде чем мы начнем, давайте рассмотрим некоторые моменты знаний CSS Transform ~

Transform

Система координат

image.png

Ось X: левый верхний угол экрана является исходной точкой, а горизонтальное направление — осью X.

Ось Y: левый верхний угол экрана является исходной точкой, а вертикальное направление — осью Y.

Ось Z: левый верхний угол экрана — это начало координат, а ось, перпендикулярная компьютеру, — это ось Z, которую можно понимать как ось, указывающую на нас.

Порядок выполнения параметров преобразования

Эффект, переданный в преобразовании,порядок исполнения(фактически это можно понимать какпост-входящийизвыполнить первым, но фактический расчетматрица:matrixспособ расчета), и преобразование изменит ось координат.

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

image.png

Существует два матричных метода:
1. матрица () матрица 3x3
2. matrix3d() матрица 4x4

Например:

transform: rotate(30deg);

// matrix(cos30°,sin30°,-sin30°,cos30°,0,0);
transform: matrix(0.866025,0.500000,-0.500000,0.866025,0,0);

Результаты производительности этих двух методов согласуются, но если вы хотите реализовать вращение с чистой матрицей, вам нужно вручную вычислить различные значения sin и cos.

Вот еще два простых примера, которые помогут быстро понять порядок вычисления (выполнения) преобразований:

  1. Сначала выполните команду scaleX(0.5), чтобы превратить квадрат в прямоугольник, а затем выполните команду rotateZ(45deg), чтобы повернуть элемент на 45 градусов по часовой стрелке, в результате чего получится наклонный прямоугольник:
.test_transform {
  width:100px;
  height:100px;
  background-color: #c685d9;
  transform: rotate(45deg) scaleX(0.5);
}

image.png

  1. Сначала выполните поворот на 45 градусов по часовой стрелке, затем сожмите ось X и получите ромб:
.test_transform_2 {
  ...
  transform: scaleX(0.5) rotate(45deg);
}

image.png

rotate

Простое вращение, rotate(45deg) на самом деле rotateZ(45deg).

image.png

scale

Масштаб, как масштабY(0,6)

image.png

scale() работает только для преобразований на евклидовой плоскости (двумерной плоскости). Если требуется масштабирование в пространстве, необходимо использовать scale3D().

Вот как реализовать простой эффект обтекания одним шариком с помощью CSS:

Для достижения эффекта объемного звучания одного шара

Шаг 1 - Основной стиль

image.png

<div className='wrap'>
  <div className='planet'>
    <div className='ball' />
  </div>
</div>
.wrap {
  display: flex;
  background-image: linear-gradient(180deg, #020205 0%, #170f39 51%, #35247a 95%);
  width: 600px;
  height: 600px;
  align-items: center;
  justify-content: center;
}

.planet {
  position: absolute;
  border: 2px solid #fff;
  transform-style: preserve-3d;
  width: 200px;
  height: 200px;
}

.ball {
  width: 50px;
  height: 50px;
  position: absolute;
  border-radius: 50%;
  background-color: yellowgreen;
}

Шаг 2 - Проведите круг по дорожке

image.png

.ball {
  // ...
  left: calc(50% - 25px);
  top: -25px;
}

Зачем нужен этот шаг? Если к этому квадрату добавить border-radius: 50% и превратить его в круг, то текущая точка на картинке нанизывается на круговую дорожку, и радиус, соответствующий четырем углам квадрата, будет больше радиуса круг. .

Шаг 3 - Поверните дорожку

image.png

.planet {
  transform: rotateZ(45deg);
}

.ball {
  transform: rotateZ(-45deg); // 中和轨道的旋转
}

Шаг 4 — Сожмите ось Y дорожки, чтобы создать 3D-эффект.

Обратите внимание на последовательность, нужно сначала повернуть, а потом сжать, иначе получится наклонный прямоугольник

image.png

.planet {
  transform: scaleY(0.5) rotateZ(45deg);
}

.ball {
  // 中和轨道的 scaleY 压缩,2 * 0.5 = 1 恢复原状,注意传入顺序,和 .planet 的 transform 是相反的,就像连续上了几个不同的锁,打开时要用和上锁相反的顺序去解
  transform: rotateZ(-45deg) scaleY(2);
}

Шаг 5 — Превратите дорожку в эллипс

image.png

.planet {
  border-radius: 50%;
}

Шаг 6 - Заставьте трек вращаться

Вышеуказанные шаги превратили исходный график в график, похожий на орбиты и планеты.Пока соблюдаются приведенные выше правила нейтрализации для rotateZ и scaleY, орбиту можно вращать, не сжимая стиль сферы:

1616053387270-7ecf4769-40b5-4cb0-8461-1f79bd99586e.gif

// 公转动画
@keyframes planet-rotate {
  0% {
    transform:  scaleY(0.5) rotate(0);
  }
  100% {
    transform:  scaleY(0.5) rotate(360deg);
  }
}

// 自转动画
@keyframes self-rotate {
  0% {
    transform: rotate(0) scaleY(2);
  }
  100% {
    transform: rotate(-360deg) scaleY(2);
  }
}

.planet {
  animation: planet-rotate 20s linear infinite;
}

.ball {
  animation: self-rotate 20s linear infinite;
}

Шаг 7 - Сделайте угол наклона гусеницы

По-прежнему используя порядок выполнения преобразования, пока вы выполняете поворот (Z) в конце, вы можете сделать всю плоскость наклонной.

1616053594414-87104715-d988-458a-9918-5fd759f88f42.gif

@keyframes planet-rotate {
  0% {
    transform:  rotate(45deg) scaleY(0.5) rotate(0);
  }
  100% {
    transform:  rotate(45deg) scaleY(0.5) rotate(360deg);
  }
}

@keyframes self-rotate {
  0% {
    transform: rotate(0) scaleY(2) rotate(-45deg);
  }
  100% {
    transform: rotate(-360deg) scaleY(2) rotate(-45deg);
  }
}

Достичь эффекта многошарового объемного звучания

Поскольку контейнер с дорожками может гарантировать, что максимум четыре шарика будут двигаться по круговой дорожке, если вы хотите добиться движения более 4-х шариков, на самом деле вам нужно только перекрыть несколько дорожек + плоскость шарика, но только отображать одну дорожку (границу) т.е. можно.

спортивная модель

независимое движение= одиночная сфера + дорожка одиночной сферы (родительский элемент)
многошаровое окружение= Независимые движущиеся особи * N перекрываются в одной и той же позиции, и отображается только орбита нижней сферы, остальные орбиты скрыты, и, наконец, начальная позиция вращения смещается для каждой независимо движущейся особи

Этапы реализации

Ниже с5Возьмите сцену одного шара в качестве примера, чтобы представить, как добиться эффекта нескольких шаров.Для удобства написания используется React + Sass:

1. Напишите базовую структуру и стили DOM

Jsx

// 传入数据
const dataSource = [
  {
    name: '山田',
  },
  // ...
];

// 渲染一个球 + 名字的 DOM
const renderCircleBoxItem = (name: string) => {
  return (
    <div className={styles.circleBoxItem}>
      <div className={styles.ball} />
      <div className={styles.name}>{name}</div>
    </div>
  );
};

// 根据 dataSource 的数量来渲染多个旋转体
<div className={styles.circleBoxWrap}>
  {
    dataSource.map((item, key) => (
      <div key={key} className={styles.circleBox}>{renderCircleBoxItem(item.name)}</div>
    ))
  }
</div>

Sass

// 轨道层 keyframes
@function getPlanetRotate($rotateValue) {
  @return rotate(45deg) scaleY(0.5) rotate(#{$rotateValue});
}

@keyframes planet-rotate {
  0% {
    transform: getPlanetRotate(0deg);
  }
  100% {
    transform: getPlanetRotate(360deg);
  }
}

// 自转球体 keyframes
@function getSelfRotate($rotateValue) {
  @return rotate(#{$rotateValue}) scaleY(2) rotate(-45deg) scale(1)) translateX(50px);
  // 这里 translateX 是为了修正球的位置,使之尽量保持在轨道上运动
}

@keyframes self-rotate {
  0% {
    transform: getSelfRotate(0deg);
  }
  100% {
    transform: getSelfRotate(-360deg);
  }
}

// 轨道元素,内含一个球体
.circleBox {
  // 统一转动速度
  $planet-rotate-speed: 30s;
  
  // 随便定个轨道大小
  width: 648px;
  height: 648px;
  position: absolute;
  transform-style: preserve-3d;
  	
  // 让轨道呈圆形
  border-radius: 50%;

  // 球体元素(球 + 文字 label)
  .circleBoxItem {
    position: absolute;
    display: flex;
    flex-direction: row;
    align-items: center;
      
    // 位置修正,要让球 + 文字的单元处于父元素(不加 border-radius 时是一个正方形)的一边的中心位置
    //,这样才能在形成椭圆轨道时,始终贴合轨道运动
    width: 200px; // 球 + 文字定宽,方便计算位置修正
    top: -30px; // 球的直径是 60px,向上偏移一半
    left: calc(50% - #{p2r(100)}); // 横向居中
      
    .ball {
      width: 60px;
      height: 60px;
      border-radius: 50%;
      overflow: hidden;
      border: 6px solid #fff;
      background-color: #6d45ca;
      margin-right: p2r(20);
    }
  
    .name {
      // 文字相关的样式...
    }
  }

  &:nth-child(1) {
    border: p2r(2) solid #fff;
    
    animation: planet-rotate $planet-ratate-speed linear infinite;
    .circleBoxItem {
      animation: self-rotate $planet-ratate-speed linear infinite;
    }
  }
}

2. Обработка смещений дорожек

Чтобы компенсировать сферы, каждаянезависимое движениеНачальная позиция поворота производит вычисление смещения, перезаписывая ключевые кадры дорожки:

Sass

// 使用 css 变量控制步长(即偏移距离)
:root {
  --planet-rotate-step: 72deg; // 72 = 360 / 5
}

@function getPlanetRotate($rotateValue) {
  @return rotate(45deg) scaleY(0.5) rotate(#{$rotateValue});
}

@keyframes planet-rotate-1 {
  0% {
    transform: getPlanetRotate(0deg);
  }
  100% {
    transform: getPlanetRotate(360deg);
  }
}

@keyframes planet-rotate-2 {
  0% {
    transform: getPlanetRotate(calc(0deg + var(--planet-rotate-step) * 1));
  }
  100% {
    transform: getPlanetRotate(calc(360deg + var(--planet-rotate-step) * 1));
  }
}

@keyframes planet-rotate-3 {
  0% {
    transform: getPlanetRotate(calc(0deg + var(--planet-rotate-step) * 2));
  }
  100% {
    transform: getPlanetRotate(calc(360deg + var(--planet-rotate-step) * 2));
  }
}

// ...

3. Относитесь к движению сферы

Сферу нужно скорректировать на траекторию вращения орбиты, а ключевые кадры сферы нужно записать:

@function getSelfRotate($rotateValue) {
  @return rotate(#{$rotateValue}) scaleY(2) rotate(-45deg) scale(1) translateX(50px);
}

@keyframes self-rotate-1 {
  0% {
    transform: getSelfRotate(0deg);
  }
  100% {
    transform: getSelfRotate(-360deg);
  }
}

@keyframes self-rotate-2 {
  0% {
    transform: getSelfRotate(calc(0deg - var(--planet-rotate-step) * 1));
  }
  100% {
    transform: getSelfRotate(calc(-360deg - var(--planet-rotate-step) * 1));
  }
}

@keyframes self-rotate-3 {
  0% {
    transform: getSelfRotate(calc(0deg - var(--planet-rotate-step) * 2));
  }
  100% {
    transform: getSelfRotate(calc(-360deg - var(--planet-rotate-step) * 2));
  }
}

// ...

4. Написание заявления об анимации

Настройте анимацию элемента:

.circleBox {
  &:nth-child(1) {
    // 仅显示第一个轨道
    border: p2r(2) solid #fff;

    animation: planet-rotate-1 $planet-rotate-speed linear infinite;
    .circleBoxItem {
      animation: self-rotate-1 $planet-rotate-speed linear infinite;
    }
  }

  &:nth-child(2) {
    animation: planet-rotate-2 $planet-rotate-speed linear infinite;
    .circleBoxItem {
      animation: self-rotate-2 $planet-rotate-speed linear infinite;
    }
  }

  &:nth-child(3) {
    animation: planet-rotate-3 $planet-rotate-speed linear infinite;
    .circleBoxItem {
      animation: self-rotate-3 $planet-rotate-speed linear infinite;
    }
  }
  
  // ...
}

5. Автоматически рассчитывайте расстояние между шариками с помощью переменных CSS.

Реальная сцена должна автоматически обрабатывать расстояние между мячами (расстояние смещения) в соответствии с количеством входящих данных.В настоящее время JS можно использовать для динамического расчета и изменения переменной CSS прямо сейчас, чтобы решить ее идеально:

const number = dataSource.length;
const step = 360 / number;

document.documentElement.style.setProperty('--planet-rotate-step', `${step}deg`);

1616045664764-f8401335-1219-43c2-96e5-35fab4e8011f.gif

Анимация CSS вокруг планеты завершена, спасибо за просмотр, увидимся в следующем выпуске~


Набор в школу 2021 года (для выпускников 2022 года) официально начался! Свяжитесь с нами, пожалуйста, укажите источник Nuggets, смотрите картинку ниже👇 для получения дополнительной информации~

image.png