Делаем Шар судьбы на CSS
medium

Делаем Шар судьбы на CSS

Получаем ответы на все вопросы

Есть такая игрушка — Шар судьбы. Внутри шара в жидкости плавает многогранник, на каждой грани которого написан какой-то ответ. Мы берём шар, формулируем вопрос, трясём шар, многогранник всплывает какой-то стороной. Мы видим ответ, который появился в прозрачном окошке.

Сегодня мы сделаем такой же шар — его можно потрясти и сразу узнать все ответы на любые вопросы. Изначально мы планировали сделать такое для «Кинжала» в формате профориентационного гадания, но шутка затянулась, поэтому просто показываем, как это сделано.

Делаем Шар судьбы на CSS

Как всё устроено

Будем собирать проект без JavaScript — это значит, что всё действие будет происходить внутри CSS-файла. 

Главная хитрость магического шара в том, что пользователь сам выбирает ответ, который он увидит при встряске. Дело в том, что в CSS нет генератора случайных чисел и нам нужно придумать что-то похожее на это. Работать всё будет так:

  1. Добавляем на форму много радиокнопок — переключателей, где можно выбрать только одно значение из всех. 
  2. Ещё добавляем кнопку сброса — при нажатии она сбросит выбранное значение в переключателях.
  3. В эти радиокнопки записываем ответы, которые даёт шар.
  4. Делаем блок с шаром, чтобы его нарисовать.
  5. В этом блоке создаём таблицу с подписями к радиокнопкам и привязываем подписи к ним с помощью свойства for=""
  6. В CSS-файле сделаем анимацию, которая несколько раз в секунду по кругу будет сдвигать нашу таблицу так, чтобы в шаре всегда оставалась только одна подпись.
  7. Когда пользователь для встряски шара щёлкнет мышкой по шару, в это время на самом деле он случайным образом выберет следующий ответ из таблицы.
  8. По следующему щелчку запускаем анимацию встряски и показываем выбранный ответ.
  9. Одновременно с этим превращаем весь шар в виртуальную кнопку сброса. При нажатии мы разворачиваем шар лицевой стороной с восьмёркой к пользователю, а при втором нажатии — снова незаметно выбираем новый ответ и трясём шар.

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

Готовим страницу

Возьмём пустой шаблон и добавим туда радиокнопки с ответами и кнопку сброса. Сразу подключим нормализатор CSS — благодаря ему страница будет выглядеть одинаково в любом браузере.

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Шар судьбы 🎱</title>
  <!-- нормализатор CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <!-- наши стили -->
  <link rel="stylesheet" href="style.css">

</head>
<body>
<!-- раздел с вариантами ответов -->
<form>
  <!-- ответы -->
  <input type="radio" name="answer" id="Да" value="Да"/><span>Да</span>
  <input type="radio" name="answer" id="Кажется, сработает" value="Кажется, <br> сработает"/><span>Кажется,<br/>сработает</span>
  <input type="radio" name="answer" id="Не в этот раз" value="Не в этот раз"/><span>Не в этот<br>раз</span>
  <input type="radio" name="answer" id="Разумеется" value="Разумеется"/><span>Разумеется</span>
  <input type="radio" name="answer" id="Нет" value="Нет"/><span>Нет</span>
  <input type="radio" name="answer" id="Надо подождать" value="Надо подождать"/><span>Надо<br/>подождать</span>
  <input type="radio" name="answer" id="Скорее всего" value="Скорее всего"/><span>Скорее<br/>всего</span>
  <input type="radio" name="answer" id="Плохая идея" value="Плохая идея"/><span>Плохая<br/>идея</span>
  <input type="radio" name="answer" id="Однозначно" value="Однозначно"/><span>Однозначно</span>
  <input type="radio" name="answer" id="Перепроверь и действуй" value="Перепроверь и действуй"/><span>Перепроверь<br/>и действуй</span>
  <input type="radio" name="answer" id="Встряхни шар ещё раз" value="Встряхни шар ещё раз"/><span>Встряхни <br/> шар<br/> ещё раз</span>
  <input type="radio" name="answer" id="Рискованно" value="Рискованно"/><span>Рискованно</span>
  <input type="radio" name="answer" id="Измени вопрос" value="Измени вопрос"/><span>Измени<br/>вопрос</span>
  <input type="radio" name="answer" id="Действуй" value="Действуй"/><span>Действуй</span>
  <input type="radio" name="answer" id="Всё не так просто" value="Всё не так просто"/><span>Всё<br/> не так<br/>просто</span>
  
  <!-- кнопка сброса -->
  <input type="reset"/>
</form>

</body>
</html>
Делаем Шар судьбы на CSS

Это заготовка ответов для шара. То, что она выглядит некрасиво, нам неважно — мы скроем все эти элементы и будем работать только с их значениями. 

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

<!-- шар -->
<div class="eight__backdrop"></div>
<!-- раздел с восьмёркой -->
<div class="eight">
  <!-- лицевая сторона -->
  <div class="eight__fascia">
    <!-- рисуем восьмёрку -->
    <div class="eight__number">8</div>
  </div>
  <!-- обратная сторона -->
  <div class="eight__fascia">
    <!-- окно с ответом -->
    <div class="eight__window"></div>
  </div>
  <!-- таблица c названиями ответов -->
  <ul>
    <li>
      <label for="Да"></label>
    </li>
    <li>
      <label for="Кажется, сработает"></label>
    </li>
    <li>
      <label for="Не в этот раз"></label>
    </li>
    <li>
      <label for="Разумеется"></label>
    </li>
    <li>
      <label for="Нет"></label>
    </li>
    <li>
      <label for="Надо подождать"></label>
    </li>
    <li>
      <label for="Скорее всего"></label>
    </li>
    <li>
      <label for="Плохая идея"></label>
    </li>
    <li>
      <label for="Однозначно"></label>
    </li>
    <li>
      <label for="Перепроверь и действуй"></label>
    </li>
    <li>
      <label for="Встряхни шар ещё раз"></label>
    </li>
    <li>
      <label for="Рискованно"></label>
    </li>
    <li>
      <label for="Измени вопрос"></label>
    </li>
    <li>
      <label for="Действуй"></label>
    </li>
    <li>
      <label for="Всё не так просто"></label>
    </li>
  </ul>
</div>
Делаем Шар судьбы на CSS
У подписей к радиокнопкам нет значений, потому что они нам не нужны — при нажатии подпись сама определит, к какой радиокнопке она относится, и включит её

Задаём общие стили

На странице мы подключили файл стилей style.css — теперь будем работать с ним. В первую очередь добавим общие настройки страницы и формы, а заодно укажем, что размеры будут относиться не к элементам, а к блокам, внутри которых они находятся:

/* свойства width и height пусть задают размеры не контента, а блока */
* {
  box-sizing: border-box;
}

/* общие настройки страницы */
body {
  /* располагаем элементы по центру */
  display: flex;
  align-items: center;
  /* настройки шрифта */
  font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
  justify-content: center;
  text-align: center;
  letter-spacing: 1px;
  font-size: 0.8rem;
  /* страница занимает всю высоту окна */
  min-height: 100vh;
  /* фон */
  background: #db0a5b;
}

/* настройки виртуальной формы */
form {
  /* радиус скругления */
  border-radius: 100%;
  /* меняем внешний вид курсора */
  cursor: pointer;
  /* размеры блока */
  height: 300px;
  width: 300px;
  /* используем относительное позиционирование */
  position: relative;
}

/* меняем внешний вид курсора на всех элементах внутри виртуальной формы  */
form * {
  cursor: pointer;
}
Делаем Шар судьбы на CSS

Видно, что форма постаралась принять форму круга, потому что мы поставили радиус скругления 100%. Теперь уберём все значки радиокнопок, чтобы остались только надписи, и кнопку сброса. Для этого сделаем их невидимыми и зададим нужные размеры:

/* скрываем все радиокнопки и виртуально выравниваем их по левому краю */
[type='radio'] {
  display: none;
  left: 100%;
  position: absolute;
}

/* настройки виртуальной кнопки сброса */
[type='reset'] {
  /* скрываем её с экрана */
  display: none;
  /* размеры блока сброса */
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  /* делаем кнопку сброса прозрачной */
  opacity: 0;
  /* ставим её поверх остальных элементов */
  z-index: 6;
  /* используем абсолютное позиционирование */
  position: absolute;
}
Делаем Шар судьбы на CSS

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

  1. Возьмём высоту блока с шаром.
  2. Раздвинем все элементы друг от друга на такое же расстояние.
  3. Первый элемент таблицы поставим на один уровень с шаром, остальные будут ниже.

Это нам даст возможность сдвигать наверх по очереди все подписи к радиокнопкам, чтобы пользователь нажимал их случайным образом. Теперь добавим нужные параметры в стили:

/* настройки элементов списка */
li {
  /* убираем отступы */
  margin: 0;
  padding: 0;
  /* задаём размеры */
  height: 300px;
  width: 300px;
}

/* настройки подписей к радиокнопкам */
label {
  /* используем блочный элемент */
  display: block;
  /* задаём размеры */
  height: 100%;
  width: 100%;
}

/* настройки таблицы с ответами */
ul {
  /* задаём плавающую анимацию ответов для разных движков браузера */
  -webkit-animation: scale 7s infinite steps(20);
          animation: scale 7s infinite steps(20);
  /* положение и размеры */
  left: 0;
  top: 0;
  width: 100%;
  /* убираем отступы */
  margin: 0;
  padding: 0;
  /* используем абсолютное позиционирование */
  position: absolute;
  /* располагаем их на слое пониже, под кнопкой сброса */
  z-index: 5;
}
Делаем Шар судьбы на CSS
Подписи равномерно распределились вниз по странице, а первый элемент списка стал на том же уровне, что и ответы

Рисуем шар

Сначала зададим общие параметры шара — размеры, размещение на странице и сам шар. Чтобы всё выглядело натурально, добавим градиент на шар — так он станет объёмнее:

/* настройки восьмёрки */
.eight {
  /* радиус скругления */
  border-radius: 100%;
  /* высота и ширина */
  width: 100%;
  height: 100%;
  /* скрываем всё, что выходит за пределы блока */
  overflow: hidden;
  /* используем относительное позиционирование */
  position: relative;
  /* располагаем восьмёрку ещё ниже слоем, под слоем с таблицей */
  z-index: 4;
}

/* фон шара */
.eight__backdrop {
  /* делаем градиентный фон */
  background: radial-gradient(circle at 5% 5%, #666, #222 50%), #222;
  /* радиус скругления */
  border-radius: 100%;
  /* размеры и положение элемента */
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  /* используем абсолютное позиционирование */
  position: absolute;
}
Делаем Шар судьбы на CSS
Если присмотреться, можно увидеть чёрную маленькую восьмёрку на шаре

Теперь оформим восьмёрку — сделаем её заметнее и поставим на лицевую сторону. Сразу добавим окно с будущими ответами — оно нам не будет мешать, потому что мы его временно подвинем в сторону и его не будет видно.

/* настройки числа 8 на шаре */
.eight__number {
  /* выравниваем всё по центру */
  justify-content: center;
  align-items: center;
  /* настройки фона и шрифта */
  background: #fff;
  border-radius: 100%;
  display: flex;
  font-size: 125px;
  /* размеры и положение элемента */
  width: 50%;
  height: 50%;
  top: 50%;
  left: 50%;
  /* используем абсолютное позиционирование */
  position: absolute;
  /* сдвигаем восьмёрку наверх */
  transform: translate(-50%, -50%);
}

/* окно с ответами */
.eight__window {
  /* фон и граница */
  background: radial-gradient(transparent, #000);
  border: 10px double #555;
  /* радиус скругления */
  border-radius: 100%;
  /* высота и положение элемента */
  width: calc(50% + 20px);
  height: calc(50% + 20px);
  left: 50%;
  position: absolute;
  top: 50%;
  /* сдвигаем окно с ответом наверх */
  transform: translate(-50%, -50%);
}

/* размеры лицевой части восьмёрки */
.eight__fascia {
  height: 300px;
  position: relative;
  width: 300px;
}
Делаем Шар судьбы на CSS

А теперь интересный момент: восьмёрка с лицевой стороной шара съехала вниз. Чтобы её вернуть назад, добавим блок, который отвечает за внешний вид ответов. Когда мы его добавим, он вернёт восьмёрку на место благодаря позиционированию и выравниванию по центру:

/* настройки внешнего вида ответов */
span {
  /* цвет фона, текста и тени */
  background: #00f;
  color: #fff;
  text-shadow: 1px 1px 0 #bfbfbf;
  /* используем абсолютное позиционирование */
  position: absolute;
  /* размеры и положение блока */
  top: 50%;
  left: 50%;
  width: 45%;
  height: 45%;
  /* сдвигаем текст */
  transform: translate(-50%, -50%);
  /* переводим текст в верхний регистр */
  text-transform: uppercase;
  /* выравниваем всё по центру */
  display: flex;
  align-items: center;
  justify-content: center;
  /* делаем ответ полностью прозрачным */
  opacity: 0;
  /* настраиваем анимацию колебаний в разных движках */
  -webkit-animation-duration: 10s;
          animation-duration: 10s;
  -webkit-animation-timing-function: linear;
          animation-timing-function: linear;
  -webkit-animation-iteration-count: infinite;
          animation-iteration-count: infinite;
  -webkit-animation-name: floaty;
          animation-name: floaty;
  /* располагаем блок с ответами ниже всех предыдущих */
  z-index: 2;
}
Делаем Шар судьбы на CSS

В оригинальном шаре ответы появляются на фоне синих треугольников, поэтому сделаем точно так же. Ещё нужно учесть, что половина треугольников в многоугольнике внутри шара смотрит острым концом наверх, а вторая половина — вниз. Чтобы это реализовать, используем псевдокласс nth-of-type() — а в скобках укажем чётные или нечётные значения:

/* треугольник с чётными ответами */
span:nth-of-type(even) {
  /* треугольник направлен вверх */
  clip-path: polygon(0 100%, 50% 20%, 100% 100%);
  -webkit-clip-path: polygon(0 100%, 50% 20%, 100% 100%);
  /* выравниваем по низу треугольника */
  align-items: flex-end;
  /* отступ и размер */
  padding-bottom: 5%;
  top: 40%;
}


/* треугольник с нечётными ответами */
span:nth-of-type(odd) {
  /* треугольник направлен вниз */
  clip-path: polygon(0 0, 50% 80%, 100% 0);
  -webkit-clip-path: polygon(0 0, 50% 80%, 100% 0);
  /* выравниваем по верху треугольника */
  align-items: flex-start;
  /* отступ и размер */
  padding-top: 5%;
  top: 60%;
}

Добавляем игровую механику

Сделаем так, чтобы шар мог реагировать на нажатия мыши:

  1. По первому нажатию встряхиваем шар и получаем ответ.
  2. Если ответ виден — по нажатию скрываем ответ и разворачиваем шар лицевой стороной (показываем восьмёрку).
  3. Если видна восьмёрка, то по нажатию встряхиваем шар снова.

Для этого мы будем смотреть, выбран ли какой-то элемент среди радиокнопок, и одновременно проверять, какая сторона видна пользователю — лицевая или окошко с ответами. В этом нам поможет селектор ~ — он смотрит, есть ли у этих элементов общий родитель, а если попроще, располагаются ли они внутри одного и того же блока:

/* если видна восьмёрка и щёлкнули по ней или по шару в целом */
[type='radio']:checked ~ .eight,
[type='radio']:checked ~ .eight__backdrop {
  /* запускаем анимацию встряски шара */
  -webkit-animation: shake 0.25s 4;
          animation: shake 0.25s 4;
}

/* если щёлкнули по ответу */
[type='radio']:checked ~ .eight .eight__fascia {
  /* показываем восьмёрку */
  transform: translate(0, -100%);
  transition: transform 0.25s 1.25s ease;
}

/* если шар встряхнули, показываем выбранный ответ — делаем его видимым */
[type='radio']:checked + span {
  opacity: 1;
  transition: opacity 1s 1.7s;
}

/* если нажали кнопку сброса */
[type='radio']:checked ~ [type='reset'] {
  /* превращаем это в блочный элемент */
  display: block;
}
Делаем Шар судьбы на CSS

Сейчас есть две проблемы — вроде всё работает, но нам выдаётся один и тот же ответ и нет анимации встряхивания. Один и тот же ответ получается из-за того, что у нас ещё нет анимации сдвига таблицы с подписями, — исправим это и добавим её в конец css-файла:

/* анимация для выбора следующего элемента */
@-webkit-keyframes scale {
  to {
    transform: translateY(-100%);
  }
}
@keyframes scale {
  to {
    transform: translateY(-100%);
  }
}

Теперь ответы будут разными, но анимации всё равно ещё нет. Исправим это на последнем шаге.

Включаем анимацию

Для анимации мы сразу пропишем все команды для двух браузерных движков, включая Webkit, — это позволит сделать одинаковое поведение анимации в любом браузере:

/* анимация плавающих ответов в разных движках */
@-webkit-keyframes floaty {
  0%, 100% {
    transform: translate(-50%, -50%);
  }
  25% {
    transform: translate(-50%, -50%) translate(-2%, 2%) rotate(2deg);
  }
  50% {
    transform: translate(-50%, -50%) translate(2%, -2%) rotate(-2deg);
  }
  75% {
    transform: translate(-50%, -50%) translate(1%, 1%) rotate(1deg);
  }
}

@keyframes floaty {
  0%, 100% {
    transform: translate(-50%, -50%);
  }
  25% {
    transform: translate(-50%, -50%) translate(-2%, 2%) rotate(2deg);
  }
  50% {
    transform: translate(-50%, -50%) translate(2%, -2%) rotate(-2deg);
  }
  75% {
    transform: translate(-50%, -50%) translate(1%, 1%) rotate(1deg);
  }
}

/* анимация встряски шара в разных движках */
@-webkit-keyframes shake {
  0%, 100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(10px, 5px);
  }
  75% {
    transform: translate(-10px, -5px);
  }
}

@keyframes shake {
  0%, 100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(10px, 5px);
  }
  75% {
    transform: translate(-10px, -5px);
  }
}

Теперь всё готово: можно загадывать вопрос, трясти шар и узнавать ответ.

Узнать все ответы на странице проекта.

Делаем Шар судьбы на CSS

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Шар судьбы 🎱</title>
  <!-- нормализатор CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <!-- наши стили -->
  <link rel="stylesheet" href="style.css">

</head>
<body>
<!-- раздел с вариантами ответов -->
<form>
  <!-- ответы -->
  <input type="radio" name="answer" id="Да" value="Да"/><span>Да</span>
  <input type="radio" name="answer" id="Кажется, сработает" value="Кажется, <br> сработает"/><span>Кажется,<br/>сработает</span>
  <input type="radio" name="answer" id="Не в этот раз" value="Не в этот раз"/><span>Не в этот<br>раз</span>
  <input type="radio" name="answer" id="Разумеется" value="Разумеется"/><span>Разумеется</span>
  <input type="radio" name="answer" id="Нет" value="Нет"/><span>Нет</span>
  <input type="radio" name="answer" id="Надо подождать" value="Надо подождать"/><span>Надо<br/>подождать</span>
  <input type="radio" name="answer" id="Скорее всего" value="Скорее всего"/><span>Скорее<br/>всего</span>
  <input type="radio" name="answer" id="Плохая идея" value="Плохая идея"/><span>Плохая<br/>идея</span>
  <input type="radio" name="answer" id="Однозначно" value="Однозначно"/><span>Однозначно</span>
  <input type="radio" name="answer" id="Перепроверь и действуй" value="Перепроверь и действуй"/><span>Перепроверь<br/>и действуй</span>
  <input type="radio" name="answer" id="Встряхни шар ещё раз" value="Встряхни шар ещё раз"/><span>Встряхни <br/> шар<br/> ещё раз</span>
  <input type="radio" name="answer" id="Рискованно" value="Рискованно"/><span>Рискованно</span>
  <input type="radio" name="answer" id="Измени вопрос" value="Измени вопрос"/><span>Измени<br/>вопрос</span>
  <input type="radio" name="answer" id="Действуй" value="Действуй"/><span>Действуй</span>
  <input type="radio" name="answer" id="Всё не так просто" value="Всё не так просто"/><span>Всё<br/> не так<br/>просто</span>
  <!-- шар -->
  <div class="eight__backdrop"></div>
  <!-- раздел с восьмёркой -->
  <div class="eight">
    <!-- лицевая сторона -->
    <div class="eight__fascia">
      <!-- рисуем восьмёрку -->
      <div class="eight__number">8</div>
    </div>
    <!-- обратная сторона -->
    <div class="eight__fascia">
      <!-- окно с ответом -->
      <div class="eight__window"></div>
    </div>
    <!-- таблица c названиями ответов -->
    <ul>
      <li>
        <label for="Да"></label>
      </li>
      <li>
        <label for="Кажется, сработает"></label>
      </li>
      <li>
        <label for="Не в этот раз"></label>
      </li>
      <li>
        <label for="Разумеется"></label>
      </li>
      <li>
        <label for="Нет"></label>
      </li>
      <li>
        <label for="Надо подождать"></label>
      </li>
      <li>
        <label for="Скорее всего"></label>
      </li>
      <li>
        <label for="Плохая идея"></label>
      </li>
      <li>
        <label for="Однозначно"></label>
      </li>
      <li>
        <label for="Перепроверь и действуй"></label>
      </li>
      <li>
        <label for="Встряхни шар ещё раз"></label>
      </li>
      <li>
        <label for="Рискованно"></label>
      </li>
      <li>
        <label for="Измени вопрос"></label>
      </li>
      <li>
        <label for="Действуй"></label>
      </li>
      <li>
        <label for="Всё не так просто"></label>
      </li>
    </ul>
  </div>
  <!-- кнопка сброса -->
  <input type="reset"/>
</form>

</body>
</html>

/* свойства width и height пусть задают размеры не контента, а блока */
* {
  box-sizing: border-box;
}

/* общие настройки страницы */
body {
  /* располагаем элементы по центру */
  display: flex;
  align-items: center;
  /* настройки шрифта */
  font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
  justify-content: center;
  text-align: center;
  letter-spacing: 1px;
  font-size: 0.8rem;
  /* страница занимает всю высоту окна */
  min-height: 100vh;
  /* фон */
  background: #db0a5b;
}

/* настройки виртуальной формы */
form {
  /* радиус скругления */
  border-radius: 100%;
  /* меняем внешний вид курсора */
  cursor: pointer;
  /* размеры блока */
  height: 300px;
  width: 300px;
  /* используем относительное позиционирование */
  position: relative;
}

/* меняем внешний вид курсора на всех элементах внутри виртуальной формы  */
form * {
  cursor: pointer;
}

/* скрываем все радиокнопки и виртуально выравниваем их по левому краю */
[type='radio'] {
  display: none;
  left: 100%;
  position: absolute;
}

/* настройки виртуальной кнопки сброса */
[type='reset'] {
  /* скрываем её с экрана */
  display: none;
  /* размеры блока сброса */
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  /* делаем кнопку сброса прозрачной */
  opacity: 0;
  /* ставим её поверх остальных элементов */
  z-index: 6;
  /* используем абсолютное позиционирование */
  position: absolute;
}

/* настройки элементов списка */
li {
  /* убираем отступы */
  margin: 0;
  padding: 0;
  /* задаём размеры */
  height: 300px;
  width: 300px;
}

/* настройки подписей к радиокнопкам */
label {
  /* используем блочный элемент */
  display: block;
  /* задаём размеры */
  height: 100%;
  width: 100%;
}

/* настройки таблицы с ответами */
ul {
  /* задаём плавающую анимацию ответов для разных движков браузера */
  -webkit-animation: scale 7s infinite steps(20);
          animation: scale 7s infinite steps(20);
  /* положение и размеры */
  left: 0;
  top: 0;
  width: 100%;
  /* убираем отступы */
  margin: 0;
  padding: 0;
  /* используем абсолютное позиционирование */
  position: absolute;
  /* располагаем их на слое пониже, под кнопкой сброса */
  z-index: 5;
}

/* настройки восьмёрки */
.eight {
  /* радиус скругления */
  border-radius: 100%;
  /* высота и ширина */
  width: 100%;
  height: 100%;
  /* скрываем всё, что выходит за пределы блока */
  overflow: hidden;
  /* используем относительное позиционирование */
  position: relative;
  /* располагаем восьмёрку ещё ниже слоем, под слоем с таблицей */
  z-index: 4;
}

/* фон шара */
.eight__backdrop {
  /* делаем градиентный фон */
  background: radial-gradient(circle at 5% 5%, #666, #222 50%), #222;
  /* радиус скругления */
  border-radius: 100%;
  /* размеры и положение элемента */
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  /* используем абсолютное позиционирование */
  position: absolute;
}

/* настройки числа 8 на шаре */
.eight__number {
  /* выравниваем всё по центру */
  justify-content: center;
  align-items: center;
  /* настройки фона и шрифта */
  background: #fff;
  border-radius: 100%;
  display: flex;
  font-size: 125px;
  /* размеры и положение элемента */
  width: 50%;
  height: 50%;
  top: 50%;
  left: 50%;
  /* используем абсолютное позиционирование */
  position: absolute;
  /* сдвигаем восьмёрку наверх */
  transform: translate(-50%, -50%);
}

/* окно с ответами */
.eight__window {
  /* фон и граница */
  background: radial-gradient(transparent, #000);
  border: 10px double #555;
  /* радиус скругления */
  border-radius: 100%;
  /* высота и положение элемента */
  width: calc(50% + 20px);
  height: calc(50% + 20px);
  left: 50%;
  position: absolute;
  top: 50%;
  /* сдвигаем окно с ответом наверх */
  transform: translate(-50%, -50%);
}

/* размеры лицевой части восьмёрки */
.eight__fascia {
  height: 300px;
  position: relative;
  width: 300px;
}

/* настройки внешнего вида ответов */
span {
  /* цвет фона, текста и тени */
  background: #00f;
  color: #fff;
  text-shadow: 1px 1px 0 #bfbfbf;
  /* используем абсолютное позиционирование */
  position: absolute;
  /* размеры и положение блока */
  top: 50%;
  left: 50%;
  width: 45%;
  height: 45%;
  /* сдвигаем текст */
  transform: translate(-50%, -50%);
  /* переводим текст в верхний регистр */
  text-transform: uppercase;
  /* выравниваем всё по центру */
  display: flex;
  align-items: center;
  justify-content: center;
  /* делаем ответ полностью прозрачным */
  opacity: 0;
  /* настраиваем анимацию колебаний в разных движках */
  -webkit-animation-duration: 10s;
          animation-duration: 10s;
  -webkit-animation-timing-function: linear;
          animation-timing-function: linear;
  -webkit-animation-iteration-count: infinite;
          animation-iteration-count: infinite;
  -webkit-animation-name: floaty;
          animation-name: floaty;
  /* распологаем блок с ответами ниже всех предыдущих */
  z-index: 2;
}


/* треугольник с чётными ответами */
span:nth-of-type(even) {
  /* треугольник направлен вверх */
  clip-path: polygon(0 100%, 50% 20%, 100% 100%);
  -webkit-clip-path: polygon(0 100%, 50% 20%, 100% 100%);
  /* выравниваем по низу треугольника */
  align-items: flex-end;
  /* отступ и размер */
  padding-bottom: 5%;
  top: 40%;
}


/* треугольник с нечётными ответами */
span:nth-of-type(odd) {
  /* треугольник направлен вниз */
  clip-path: polygon(0 0, 50% 80%, 100% 0);
  -webkit-clip-path: polygon(0 0, 50% 80%, 100% 0);
  /* выравниваем по верху треугольника */
  align-items: flex-start;
  /* отступ и размер */
  padding-top: 5%;
  top: 60%;
}


/* если видна восьмёрка и щёлкнули по ней или по шару в целом */
[type='radio']:checked ~ .eight,
[type='radio']:checked ~ .eight__backdrop {
  /* запускаем анимацию встряски шара */
  -webkit-animation: shake 0.25s 4;
          animation: shake 0.25s 4;
}

/* если щёлкнули по ответу */
[type='radio']:checked ~ .eight .eight__fascia {
  /* показываем восьмёрку */
  transform: translate(0, -100%);
  transition: transform 0.25s 1.25s ease;
}

/* если шар встряхнули, показываем выбранный ответ — делаем его видимым */
[type='radio']:checked + span {
  opacity: 1;
  transition: opacity 1s 1.7s;
}

/* если нажали кнопку сброса */
[type='radio']:checked ~ [type='reset'] {
  /* превращаем это в блочный элемент */
  display: block;
}

/* анимация для выбора следующего элемента */
@-webkit-keyframes scale {
  to {
    transform: translateY(-100%);
  }
}
@keyframes scale {
  to {
    transform: translateY(-100%);
  }
}

/* анимация плавающих ответов в разных движках */
@-webkit-keyframes floaty {
  0%, 100% {
    transform: translate(-50%, -50%);
  }
  25% {
    transform: translate(-50%, -50%) translate(-2%, 2%) rotate(2deg);
  }
  50% {
    transform: translate(-50%, -50%) translate(2%, -2%) rotate(-2deg);
  }
  75% {
    transform: translate(-50%, -50%) translate(1%, 1%) rotate(1deg);
  }
}

@keyframes floaty {
  0%, 100% {
    transform: translate(-50%, -50%);
  }
  25% {
    transform: translate(-50%, -50%) translate(-2%, 2%) rotate(2deg);
  }
  50% {
    transform: translate(-50%, -50%) translate(2%, -2%) rotate(-2deg);
  }
  75% {
    transform: translate(-50%, -50%) translate(1%, 1%) rotate(1deg);
  }
}

/* анимация встряски шара в разных движках */
@-webkit-keyframes shake {
  0%, 100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(10px, 5px);
  }
  75% {
    transform: translate(-10px, -5px);
  }
}

@keyframes shake {
  0%, 100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(10px, 5px);
  }
  75% {
    transform: translate(-10px, -5px);
  }
}

Текст:

Михаил Полянин

Редактор:

Максим Ильяхов

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Виталий Вебер

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
medium