Условие
Дана готовая страница с «машиной Голдберга». HTML и базовые стили уже есть. JavaScript использовать нельзя. Вёрстку трогать тоже нельзя — только дописать CSS.
Запуск анимации происходит через чекбокс #start и селекторы состояния (:checked). Дальше — через CSS-анимации и задержки. Цепочка должна устойчиво работать с повторным переключением состояния (запуск/остановка).
Видео-эталон
Точные координаты и тайминги не выводятся — нужно по видео добиться максимально похожего поведения.
Примечание
При запуске скрипт должен оставаться неподвижным, а решение должно быть устойчивым с повторному переключению состояний.
Решение
Подход
Это задача на CSS checkbox hack + цепочка анимаций с разной задержкой animation-delay.
1. Базовый «триггер»
<input type="checkbox" id="start" />
<label for="start">Старт</label>
<div class="scene">…</div>#start { display: none; }
#start:checked ~ .scene .ball {
animation: roll 2s linear forwards;
}
@keyframes roll {
from { transform: translateX(0); }
to { transform: translateX(300px); }
}2. Цепочка событий
Каждый элемент машины Голдберга стартует с задержкой относительно момента :checked:
#start:checked ~ .scene .ball {
animation: roll 1.2s linear forwards;
}
#start:checked ~ .scene .domino-1 {
animation: fall 0.4s ease-in 1.2s forwards;
}
#start:checked ~ .scene .domino-2 {
animation: fall 0.4s ease-in 1.6s forwards;
}
#start:checked ~ .scene .lever {
animation: tilt 0.8s ease-out 2.0s forwards;
}
/* … */3. Сброс при выключении чекбокса
Без forwards анимация после animation-end сбрасывается в исходное состояние. Если forwards стоит — мы остаёмся в финальном состоянии. При снятии галочки селектор #start:checked ~ ... перестанет совпадать → CSS-движок перевычислит — анимаций нет → элементы вернутся в исходные transform: ....
Чтобы не было «прыжка», добавляем переход возврата:
.ball {
transform: translateX(0);
transition: transform 0.5s linear;
}
#start:checked ~ .scene .ball {
/* Анимация перехватывает transition */
animation: roll 1.2s linear forwards;
}Когда :checked снимается — animation пропадает, transition плавно возвращает в translateX(0).
Альтернатива: анимация-возврат через @keyframes для не-:checked варианта.
4. Устойчивость к повторным переключениям
Главное правило: исходное состояние должно быть в обычных transform: ... (или базовых стилях). Не оставлять стейт «в анимации» без forwards или transition для возврата.
Полный пример
<input type="checkbox" id="start" />
<label for="start" class="btn">Запустить машину</label>
<div class="scene">
<div class="ball"></div>
<div class="domino domino-1"></div>
<div class="domino domino-2"></div>
<div class="lever"></div>
<div class="bucket"></div>
</div>.scene { position: relative; height: 300px; }
.ball, .domino, .lever, .bucket { position: absolute; transition: all 0.5s linear; }
.ball { left: 0; top: 0; width: 30px; height: 30px; background: red; border-radius: 50%; }
.domino-1 { left: 320px; top: 280px; width: 10px; height: 80px; background: brown; transform-origin: bottom; }
.domino-2 { left: 360px; top: 280px; width: 10px; height: 80px; background: brown; transform-origin: bottom; }
.lever { left: 400px; top: 250px; width: 100px; height: 10px; background: gray; transform-origin: left; }
.bucket { left: 520px; top: 200px; width: 50px; height: 50px; background: orange; }
@keyframes roll { to { transform: translateX(300px); } }
@keyframes fall { to { transform: rotate(75deg); } }
@keyframes tilt { to { transform: rotate(45deg); } }
@keyframes drop { to { transform: translateY(60px); } }
#start:checked ~ .scene .ball { animation: roll 1.2s linear forwards; }
#start:checked ~ .scene .domino-1 { animation: fall 0.4s ease-in 1.2s forwards; }
#start:checked ~ .scene .domino-2 { animation: fall 0.4s ease-in 1.6s forwards; }
#start:checked ~ .scene .lever { animation: tilt 0.8s ease-out 2.0s forwards; }
#start:checked ~ .scene .bucket { animation: drop 0.6s ease-in 2.8s forwards; }Подводные камни
animation-delay≠ синхронизация с реальными событиями. Если ball едет 1.2s, домино должна стартовать в1.2s, не в1.0s. Точные тайминги — вручную из видео.forwardsобязателен. Без него анимация откатывается → следующая анимация начнётся уже не из конечного состояния.- Невозможно «остановить» анимацию посередине через CSS.
animation-play-state: pausedчерез:checkedработает, но при сбросе:checkedанимация не сбрасывает прогресс — а полностью пропадает (откатываемся к началу). Это даёт скачок. Для плавности —transitionна возврат. ~против+.#start:checked ~ .scene .ball— общий sibling-селектор:.sceneдолжна быть ниже чекбокса в DOM. Если выше — селектор не сработает, нужен другой паттерн.transitionпобеждаетanimation? Не всегда. Если оба заданы, animation имеет приоритет, но при удалении animation transition сработает на возврат.transform-originдля домино. Без него домино «закрутится» вокруг центра, а не упадёт.- Производительность. Анимировать
transformиopacity(неtop/left/width) — они GPU-ускоренные.
Эталонный ответ
CSS checkbox hack: #start:checked ~ .scene .X { animation: ... 0.X s X.Xs forwards; } для каждого элемента сцены, с правильно подобранными animation-delay. Возврат — через transition на свойствах transform/opacity.