# Как я запилил интерактивную модель Солнечной системы на чистом JS (и ты сможешь)
# Как я запилил интерактивную модель Солнечной системы на чистом JS (и ты сможешь) --------------------- Привет, подписчики! На связи команда © DevGeek.iT, 2026 --------------------- Знаешь, что общего между космосом и фронтендом? И там, и там важно правильно рассчитать траекторию, чтобы ничего не развалилось. Недавно я выкатил на сайт интерактивную орбитальную модель — ту самую, где можно крутить время, смотреть на движение планет и даже ускорять процессы до 20 лет в секунду. **Живой пример прямо перед тобой:** заходи, пробуй, пока всё работает. https://devgeekit.github.io/-/ --------------------- Сегодня расскажу, как я её собирал, какие грабли прошёл и почему Three.js здесь — не панацея. --------------------- Сразу скажу: я не стал брать тяжёлые движки. Для такой задачи отлично подошёл обычный Canvas 2D + requestAnimationFrame. Да, трёхмерность мы эмулируем через перспективу и размеры объектов — это даёт больше контроля над производительностью, особенно на мобилках. Весь код уместился в один файл ~300 строк, и это без лишних зависимостей. --------------------- Главная механика — расчёт положения планеты по её орбите. Я использую полярные координаты: угол = текущее время * угловая скорость, а радиус фиксирован для каждой планеты. Вот базовый кусок, который крутит любую планету: ```javascript function getPlanetPosition(angle, radius) { return { x: centerX + radius * Math.cos(angle), y: centerY + radius * Math.sin(angle) }; } // В цикле анимации: planet.angle += planet.speed * deltaTime * timeScale; const pos = getPlanetPosition(planet.angle, planet.orbitRadius); // рисуем планету в pos.x, pos.y ``` Это просто, но именно здесь новички часто ошибаются: забывают умножать на `deltaTime`, из-за чего анимация начинает скакать при разной частоте кадров. Я всегда завожу отдельный таймер и передаю реальную разницу между кадрами. --------------------- **Фишка от DevGeek:** Чтобы планеты не выглядели плоскими точками, я рисую их с градиентной заливкой и небольшой тенью. Для этого использую `createRadialGradient` — это даёт эффект объёма за пару строк. А для орбит — пунктирные линии через `setLineDash`, чтобы не забивать визуал сплошными кругами. Экономит время и делает картинку «дорогой». --------------------- Управление скоростью — отдельная песня. На модели есть кнопки 60 сут/с, 1 мес/с, 1 год/с и т.д. Я реализовал их через множитель `timeScale`, который домножает базовую скорость всех планет. Но здесь подводный камень: если сделать `timeScale` слишком большим, планеты начнут «рвать» из-за дискретизации угла. Я ограничил максимальный шаг угла за кадр до 0.1 рад — если превышает, принудительно дроблю анимацию на подшаги. --------------------- **На что обратить внимание:** В модели есть переключатель «Обратная» — это обратное течение времени. Тут легко допустить ошибку с отрицательной скоростью. Я сделал отдельный флаг `reverse`, и если он включён, просто вычитаю значение из угла. Но критично — обновлять подписи планет и отображение текущей даты (1 год 243 дня) с учётом знака. Я храню общее время в днях и при изменении `timeScale` пересчитываю его аккуратно, чтобы не уйти в минус. Новички часто просто инвертируют скорость, забывая про синхронизацию счётчика дней — и получают «путешествия во времени» с багами. --------------------- Ещё я добавил подписи планет, которые всегда повёрнуты к зрителю (не вращаются вместе с орбитой). Это делается через `ctx.textAlign` и `ctx.textBaseline`, а координаты подписей я вычисляю со смещением от центра планеты. Чтобы подписи не перекрывались, для внешних планет (Уран, Нептун) я сдвигаю их наружу по радиусу — маленький, но полезный трюк. --------------------- Что касается адаптивности — модель отлично вписывается в любой экран благодаря `canvas.width/height = container.clientWidth * devicePixelRatio` и масштабированию через `ctx.scale`. Я обычно делаю так: сохраняю масштабный коэффициент и все размеры планет задаю относительно него. Это позволяе