01
Обзор
Juggernaut — браузерная реконструкция игры CRL / Pete Cooke (1985): один HTML-файл, всё рисуется на canvas 2D. Этот документ собирает визуальный язык проекта в одном месте — основы, цвет, геометрию, свет, компоненты и правила. Все образцы ниже живые: они рисуются тем же кодом, что и игра, а не вставлены картинками.
Из чего состоит
| Файл | Что внутри | Роль |
|---|---|---|
juggernaut.html | мир, физика, все виды, интерфейс, радио | единственный исходник, правится только он |
wrap.py | doctype, charset, viewport, OG-мета | сборка build/index.html |
build/ | index.html, img/, audio/01..15.mp3 | то, что уходит на сервер |
PROJECT.md | архитектура, физика, деплой, грабли | как устроен проект |
DESIGN.html | этот документ | как проект выглядит |
Чем этот язык отличается
Чертёж, а не экран
Интерфейс нарисован теми же линиями, что и мир за стеклом. Ни одной картинки в интерфейсе — только контуры, точки и градиенты.
Глубина альфой
Серых в палитре нет. Дальний план — тот же светлый тон с меньшей прозрачностью, поэтому слои не спорят по цвету.
Свет вместо заливки
Любой источник — радиальный градиент в режиме
lighter, тоном теплее самой лампы. Кружков и плашек нет.
Никакого filter
В браузере заказчика ctx.filter не
поддерживается, поэтому всё размытие подделывается тремя приёмами (раздел 07).
02
Принципы
Один холст на весь экран, никаких DOM-элементов поверх (кроме полосы тач-кнопок). Всё рисуется линиями: интерфейс — такой же чертёж, как и мир за стеклом.
- Холст масштабируется по DPR (максимум 2):
cv.width = W·DPR,ctx.setTransform(DPR,0,0,DPR,0,0). Все координаты в CSS-пикселях. - Фон —
#000305, почти чёрный с синевой. Панель приборов на полтона светлее:#05090b. - Никаких растровых текстур, кроме фотографии заставки. Плёночное зерно и дождь — тоже рисуются.
- Линия по умолчанию 1 px. Толще — только там, где нужен акцент: стрелка прибора, обводка выбранного пункта.
- Полупрозрачность вместо серого. Глубина набирается альфой одного и того же светлого тона, а не палитрой серых.
03
Цвет
Палитра из объекта C в исходнике. Три «рабочих» тона задают глубину,
остальные — смысловые: каждый цвет закреплён за сущностью и больше нигде не появляется.
Правила цвета
- Оранжевый — только текущая цель, выбранный пункт и предупреждение по времени. Если оранжевого много, значит что-то не так с иерархией.
- Салатовый и голубой разведены намеренно: ремкомплект нельзя путать с зелёным светофором, заправку — с синим сигналом навигации.
- Красный — штраф, повреждение, красный свет, зона Empty. Больше нигде.
- Четыре цвета грузов — исключение и живут только внутри модуля «ГРУЗ»:
лес
143,227,154, уголь184,166,226, нефть87,200,255, фрукты255,180,84. Они появляются исключительно заливкой колбы не ярче 40 % и никогда линией или текстом, поэтому не путаются с ролями. - Свечение теплее лампы. Ореол любого сигнала берётся на 10–15° теплее самого источника — иначе свет выглядит нарисованным.
04
Типографика
Один шрифт на весь проект — JetBrains Mono. Моноширинность держит колонки цифр и делает интерфейс похожим на приборную панель, а не на веб-страницу.
ШКАЛА КЕГЛЕЙ
Подпись модуля 8.5, вспомогательный текст 9–11, значение 13–16, крупное показание 20–34 — размер всегда считается от высоты блока, а не задаётся числом.
cap() · font(w,s)| Роль | Вес | Кегль | Цвет |
|---|---|---|---|
| Подпись модуля (ВИД, ГРУЗ) | 600 | 8.5 | mid |
| Строка данных | 500–600 | 9–11 | mid / hi |
| Значение | 700 | 13–16 | hi |
| Крупное показание (часы, частота) | 800 | 20–34 | hi / accent |
| Заголовок карточки заставки | 800 | 12.5 (ужимается) | ink на белом |
- Капс везде. Интерфейсные подписи — заглавными, это язык приборов.
- Кегль от геометрии:
clamp(Math.min(w*0.3, h*0.5), min, max)— текст переживает любую ширину модуля. - Длинное имя не режется, а ужимается до предела, и только потом
fitTxt()ставит многоточие. - Выравнивание возвращать. После
textAlign='right'обязательно вернуть'left'иtextBaseline='alphabetic'— иначе поедет соседний модуль.
05
Геометрия и ритм
Скругления — не дуги, а суперэллипс (squircle): кривизна непрерывна, угол читается объёмным. Один радиус на всю приборку, один отступ на всю сетку.
SQUIRCLE ПРОТИВ ДУГИ
Слева обычная дуга, справа суперэллипс — тот же радиус, но угол кажется толще и мягче. В проекте используется только правый.
rr() · seQuarter()| Величина | Значение | Где |
|---|---|---|
| MPAD | 12 px | зазор между модулями и внутренний отступ — одно число |
| MINSET | 4 px | отступ содержимого от рамки модуля |
| Радиус модуля | 7 px (не больше w/2, h/2) | modFrame |
| Радиус карточки заставки | 3 px | cardShell |
| Радиус клавиши | 0.20·h | keyBadge |
| Толщина линии | 1 px, акцент 1.6–2.2 | везде |
06
Свет
Источники света не рисуются кружком. Только мягкий ореол радиальным
градиентом в режиме lighter, с тоном теплее лампы.
ОРЕОЛ СИГНАЛА
Пять стопов с быстрым спадом, пульсация от игрового времени. Красный, жёлтый, зелёный — у каждого свой тёплый ореол.
glowSprite() · lighter07
Размытие без filter
В браузере заказчика ctx.filter не поддерживается. Размытие подделывается
тремя приёмами — других в проекте нет.
Градиент вместо блюра
Мягкий край — это линейный или радиальный градиент с 5–14 стопами по степенной кривой. Так сделаны кромки панели, затемнение под карточками, ореолы.
Многопроходная обводка
Внутренняя рамка модуля — четыре обводки с растущей толщиной и падающей альфой. Дёшево и выглядит как расфокус.
Оффскрин с пересэмплом
Тяжёлое размытие — рисуем в маленькую канву и растягиваем обратно. Так сделаны подложка заставки и маска растворения кромки.
Кеш спрайтов
Всё, что не меняется каждый кадр, считается один раз: стекло модуля, полоса перфорации, зерно, превью карты, шапки карточек.
08
Иконки и метки
Один набор на весь проект. Знак — тонкий контур, метафора буквальная, цвет несёт смысл. Метки мира лежат на дышащем ореоле своего цвета, иконки интерфейса — на нейтральном и вдвое слабее: так с одного взгляда видно, что объект, а что кнопка.
ИКОНКИ И МЕТКИ
Двадцать знаков одного набора. Первые два ряда — то, что встречается в игре; третий и четвёртый — расширение для сайта и экранов вокруг неё. Знак набирается тонкой обводкой (1.2–1.7 px), заливка допускается только внутри самого знака — крестом ремкомплекта, буквой поста, подложкой в 10 % у квадрата ДПС. Под меткой мира лежит дышащий ореол своего цвета, у иконок интерфейса — нейтральный и вдвое слабее. Цвет метки = смысл: цель и выбранный пункт янтарные, ремкомплект салатовый, заправка голубая, телефон красный, депо зелёное, служебное — циан. Иконки видов рисуются серым, выбранный — янтарным (справа внизу показано именно это состояние).
viewIcon() · pumpIcon() · kit / phone / police / depot markerПравила знака
- Толщина 0.9–1.7 px. Иконки видов самые тонкие (0.9), метки мира чуть плотнее (1.2–1.3), стрелка навигатора самая жирная (1.7) — она важнее всех.
- Заливка только внутри знака. Крест ремкомплекта, буква «P» у поста, подложка в 10 % под квадратом ДПС — и всё. Плашек под иконкой не бывает.
- Размер от ячейки, а не числом: иконка вида считается от стороны квадрата в модуле «ВИД».
- Состояние — цветом, не фоном. Выбранная иконка становится янтарной; ячейка получает янтарную рамку и заливку в 14 %.
- Телефон — холодный мятный
#6fe3bd, не красный. Будка зовёт взять рейс, а не предупреждает; красный в этой системе закреплён за опасностью и деньгами со знаком минус. - Новые знаки собираются из тех же примитивов: прямоугольник, скруглённый прямоугольник, дуга, отрезок. Никаких path из редактора.
| Знак | Цвет | Где встречается |
|---|---|---|
| Цель — стрелка | accent | навигатор, экран, все четыре вида |
| Телефон — будка | phone #6fe3bd | карта, все виды, навигатор |
| Ремкомплект | kit | мир, навигатор, тумблер |
| Заправка — колонка | fuel | мир, навигатор, стрелка топлива |
| Пост ДПС — квадрат с «P» | cyan | карта, вид сверху, радар |
| Депо — склад | good | цель доставки |
| Кабина — тягач сбоку | mid | прибор повреждений кабины |
| Прицеп — полуприцеп сбоку | mid | прибор повреждений прицепа |
| Четыре вида | mid / accent | модуль «ВИД» |
| Светофор, гараж, груз, смена | mid | расширенный набор, вне игры |
| Касса | good | деньги пришли |
| Штраф | bad | деньги ушли, опасность |
| Рация, волна, карта | mid / cyan | служебное и навигация |
| Компас | mid | угол вида сверху |
09
Каталог компонентов
Всё, из чего собран интерфейс. Компонент здесь — это функция рисования: у неё есть имя, обязательные параметры и место, где она появляется. Новый экран собирается из этого списка, а не из новых форм.
Примитивы
| Компонент | Функция | Что делает | Где |
|---|---|---|---|
| Суперэллипс | rr / seQuarter | скруглённый контур с непрерывной кривизной | все рамки, клавиши, ячейки |
| Ореол | glowSprite | радиальный градиент в режиме lighter | лампы, метки, фары |
| Подпись модуля | cap | капс 8.5 в левом верхнем углу | каждый модуль |
| Подгонка строки | fitTxt | ужимает и ставит многоточие | везде, где текст переменной длины |
| Зона нажатия | hot | регистрирует прямоугольник на кадр | всё кликабельное |
Элементы
| Компонент | Функция | Состояния | Где |
|---|---|---|---|
| Рамка модуля | modFrame | обычная, подсвеченная светом фар | вся приборка |
| Клавиша | keyBadge | покой, нажата, с тенью при свете | рейка над панелью |
| Рейка клавиш | keyRail | точечная линия с разрывами | над каждой группой модулей |
| Тумблер | navSw | вкл / выкл, свой цвет | навигатор |
| Стрелочный прибор | gaugeDial | крупный и мелкий, с опасной зоной | топливо, повреждения, скорость |
| Иконка вида | viewIcon | серая / выбранная | модуль «ВИД» |
| Стрелка цели | navArrow | обычная, срочная (красная) | навигатор, все четыре вида |
| Точечный эквалайзер | dotEq | играет / тишина | радио |
| Шкала настройки | tuneScale | риски + бегунок | радио, рация |
| Строка навигатора | navRow | цель, радар, ремкомплект, заправка | модуль «ВРЕМЯ» |
| Карточка заставки | cardShell | шапка с выбиванием, строки выбора | стартовый экран |
| Окно | panel | уголки, затемнение фона, зоны сброшены | телефон, заправка, карта, план |
Модули приборки
| Модуль | Клавиши | Что показывает |
|---|---|---|
| ВИД | , . V | четыре камеры, выбранная янтарная |
| МАСШТАБ | Shift < > | кратность карты и ступенчатая шкала |
| МРН | — | скорость в милях в час, зона превышения |
| РУЛЬ | ← → | угол поворота колёс |
| ПЕР | пробел R 1 2 | передача: 2, 1, задняя |
| ГРУЗ | E | сдано / план по четырём грузам |
| ВРЕМЯ | M P E U I O | часы смены, касса и навигатор с целями |
| СВЕТ | — | ближайший светофор и остаток фазы |
| ТОПЛИВО | — | бак, расход и два прибора повреждений |
| РАДИО | [ ] − + | волна, название трека, эквалайзер |
| РАЦИЯ | Shift { } _ + | частота, канал, уровень сигнала |
| ЗВУК | G B N | эффекты вкл-выкл и громкость |
10
Элементы приборки
Модуль — это стекло в рамке. Рамок две: размытая внутренняя с обратным градиентом и резкая внешняя в полпикселя. Плюс третья еле заметная кромка по верху, в двух пикселях внутрь — она и даёт ощущение толстого стекла.
РАМКА МОДУЛЯ
Заливка растворена к краям, обе рамки резкие. Верхняя кромка растворяется к углам — растворение считается в пикселях (14 px), а не в долях ширины, иначе на широком модуле она исчезает у углов.
modFrame(x,y,w,h)РЕЙКА КЛАВИШ
Точечная линия как выносная размерная, клавиши стоят на ней и разрывают её. Размер бейджей одинаковый на весь ряд: при нехватке места рейка сдвигается вбок, а не уменьшается.
keyRail() · keyBadge()КЛАВИША
Кейкап: верхняя грань квадратная, борт высокий (0.34·h), силуэт книзу расширяется. Снизу мягкая тень, её сила растёт со светом на панели. Обводка сверху лазурная, снизу салатовая — клавиша «стоит» в подсветке приборки.
keyBadge(x,y,label,sc)СТРЕЛОЧНЫЙ ПРИБОР
Дуга 0.80π→2.20π, риски на 0 / ¼ / ½ / ¾ / 1, опасная зона толстой дугой, стрелка с тёмным подбоем и втулкой. Топливо крупное, повреждения — вдвое мельче.
gaugeDial() · pumpIcon()ТУМБЛЕР НАВИГАЦИИ
Корпус с вертикальным градиентом, бегунок с бликом, включённый светится своим цветом (цель — оранжевый, ремкомплект — салатовый, заправка — голубая).
navSw(x,y,w,h,on,rgb)ПЕРЕКЛЮЧАТЕЛЬ ВИДА
Четыре квадратные ячейки 2×2, у выбранной — янтарная рамка и заливка в 14 %. Сами пиктограммы — 0.9 px, только контур; весь набор знаков собран в разделе 08 · Иконки и метки.
modView() · viewIcon(cx,cy,sz,k,on)11
Модули вживую
Вся нижняя панель по частям. Каждый модуль — стекло в рамке с подписью в левом верхнем углу; ширина считается от доступного места, содержимое — от ширины модуля. Ни один модуль не знает своих координат заранее, поэтому все размеры внутри — формулы.
ВИД
Четыре камеры в сетке 2×2. Выбранная — янтарная рамка, заливка в 14 % и янтарный контур иконки. Ячейка всегда квадрат: ширина модуля подгоняется под неё, а не наоборот.
modView() · viewIcon()МАСШТАБ
Кратность крупной цифрой, под ней ступенчатая шкала: активная ступень янтарная. Треугольник у подписи показывает, что модуль кликается.
modZoom()МРН
Скорость в милях в час. Опасная зона обведена толстой дугой — при превышении в зоне поста она загорается красным, а подпись «ДПС» начинает мигать.
modSpeed() · gaugeDial()РУЛЬ
Руль повёрнут на реальный угол колёс. Задним ходом стрелка совпадает с направлением движения — это единственное место, где знак угла инвертируется.
modSteer()ПЕР
Три позиции столбиком: вторая, первая, задняя. Текущая обведена янтарной рамкой; соседние приглушены, но остаются читаемыми.
modGear()ГРУЗ
План смены — четыре колбы, по одной на груз. Уровень растёт при разгрузке в депо, насечки внутри считают тонны плана. Цвет колбы — код категории, приглушённый до 30–40 %: он не спорит с приборами и не читается как роль. Текущий груз отмечен своим контуром и треугольником сверху, выполненный — зелёными цифрами. Итог по всей смене в шапке справа.
modCargo() · CARGOES[].rgbВРЕМЯ И НАВИГАТОР
Слева часы смены и касса, справа навигатор. Все стрелки стоят на одной вертикальной оси, текст начинается от общего левого края, расстояние прижато вправо, тумблер — у самого края.
modClock() · navRow()СВЕТ
Корпус светофора с тремя лампами. Горит только одна — свечением, а не заливкой. Рядом фаза и остаток в секундах.
modLight() · lamp()ТОПЛИВО
Крупный указатель с зоной Empty и знаком колонки, рядом расход в литрах на сто километров. Повреждения — знак, справа подпись, под ней процент, под процентом полоса уровня. Обе строки стоят на одних иксах, зазоры 10 px, линии между строками нет: строки разводит воздух. Стрелочная шкала здесь появляется, только если после знака и подписи на неё осталось место, — знак и название важнее. На совсем низком модуле топливо становится линейной шкалой, а повреждения — двумя знаками с процентом.
modGauges() · pumpIcon() · rigIcon()РАДИО
Частота крупно, под ней название трека мелкой строкой, ниже эквалайзер, внизу шкала с риской на каждой волне. Громкость — короткая линия справа от подписи.
modRadio() · dotEq() · tuneScale()РАЦИЯ
Частота в мегагерцах, канал и обрывок позывного. Столбик рисок — уровень сигнала; чем дальше собеседник, тем ниже и реже.
modCB()ЗВУК
Эффекты вкл-выкл и громкость. Состояние словом, а не иконкой: «ВКЛ» читается быстрее любого знака.
modSFX()Мелкие компоненты
СТРОКИ НАВИГАТОРА
Цель сверху в две строки — что делать и куда ехать. Ниже радары: пост ДПС, ремкомплект, заправка. У каждой строки свой цвет и свой тумблер; у цели тумблер тоже есть.
navRow() · navSw()ЭКВАЛАЙЗЕР
Сетка квадратиков, тринадцать столбцов по четыре ряда. Верхняя горящая клетка — пик, она всегда светлее. Тишина рисуется теми же клетками в 13 % — модуль не схлопывается.
dotEq()ШКАЛЫ НАСТРОЙКИ
Одна функция на радио и рацию: риски по числу позиций, у текущей риска убрана, бегунок торчит за линию вверх и вниз.
tuneScale()ПОЛОСА И ИТОГИ
Погрузка и разгрузка — полоса с янтарной заливкой и процентом справа. Итог операции всегда одной строкой: зелёная — деньги пришли, красная — ушли.
busyBar() · say()СООБЩЕНИЕ
Центрированная плашка над панелью: тёмное стекло, циановая обводка, капс. Живёт от 1.2 до 3.5 секунд и гаснет, ничего не сдвигая.
say(text, sec)КОМПАС И УКАЗАТЕЛЬ
Компас в углу вида сверху — окружность, стрелка на север, буква N. Указатель цели — та же стрелка навигатора, но на ореоле: её видно поверх любой карты.
компас · navArrow()12
Экраны и окна
Окно — это тёмное стекло с четырьмя циановыми уголками поверх размытого кадра. Уголки рисуются вместо полной рамки: они дешевле, читаются как прицел и не спорят с линиями мира. Пока окно открыто, зоны нажатия нижнего слоя сброшены.
ТЕЛЕФОН — ЗАКАЗЫ
Список рейсов: груз и получатель, расстояние, ставка. Выбранный — янтарная обводка, заливка в 10 % и стрелка слева. Внизу строка клавиш, всегда одна и та же по формату.
openPhone() · panel()ЗАПРАВКА
Фиксированные объёмы и полный бак с пересчётом под остаток. Цена справа: у выбранной строки она белая, у остальных приглушена. Недоступный по деньгам вариант не прячется, а гаснет.
openFuel() · panel()КАРТА
Улицы штрихом, поверх — метки своими цветами: телефоны красным, пост ДПС циановым квадратом, ремкомплект салатовым, заправка колонкой. Фура белым контуром, рядом стрелка цели.
drawMap() · panel()Правила окна
- Фон размывается, а не затемняется. Кадр уходит в оффскрин, уменьшается и растягивается обратно — поверх идёт лёгкое затемнение, но не чёрная плашка.
- Заголовок цветом раздела: телефон — циан, заправка — голубой, план — янтарный. Так окно узнаётся до чтения.
- Строка клавиш внизу — обязательна и одинакова по формату: действие, разделитель «·», следующее действие.
- Выбор — обводкой и заливкой в 10 %, никогда сплошной плашкой: на размытом фоне сплошная заливка выглядит дырой.
- Окно не прокручивается. Если пунктов больше, чем помещается, уменьшается высота строки, а список остаётся целиком.
21
Органы управления
Любая клавиша, до которой можно дотянуться мышью, должна быть видна как клавиша. Правило простое: что нарисовано — то и кликается.
- Зоны собираются во время отрисовки. Модуль сам регистрирует себя через
hot(x,y,w,h,fn); список пересобирается каждый кадр. - Координаты переводятся из системы модуля в экранную через
ctx.getTransform()— иначе клик уедет на высоту панели. - Окно перекрывает всё.
panel()чистит список зон, чтобы под модальным экраном не сработал нижний слой. - Мышь как джойстик: тяга по игровому полю — влево-вправо руль, вверх-вниз газ и тормоз, в точке нажатия рисуется кольцо с ручкой.
- Курсор подсказывает:
pointerнад зоной,grabнад игровым полем.
13
Кнопки и действия
Дальше идёт веб-кит: компоненты, которых в самой игре нет, но которые нужны сайту, лендингу и любому окну вокруг неё. Это уже не canvas, а обычные DOM-элементы — зато по тем же правилам: тонкая обводка, полупрозрачная заливка, цвет равен смыслу, подписи капсом. Всё ниже работает как есть, классы с префиксом j.
ИЕРАРХИЯ ДЕЙСТВИЙ
Одна янтарная кнопка на экран — главное действие. Всё остальное ниже по весу. Разрушительное — красное, и оно никогда не бывает главным.
РАЗМЕРЫ
Четыре ступени. Кегль и отступы растут вместе, радиус — только у самой крупной.
СОСТОЯНИЯ
Загрузка — три точки вместо текста, ширина не прыгает. Недоступная кнопка гаснет до 38 %, но остаётся на месте: пропадающие кнопки ломают привычку.
ИКОНКА И ГРУППА
Иконочная кнопка — квадрат со стороной строки. Группа склеивается в один ряд без зазоров: так она читается как один орган управления.
ВО ВСЮ ШИРИНУ
В узкой колонке и в окне кнопка растягивается. Текст остаётся по центру, иконка едет с ним.
Правила
- Заливка полупрозрачная, не сплошная. Кнопка — это подсвеченная рамка, а не плашка: так она живёт на любом фоне, включая размытый кадр.
- Капс и разрядка. Все подписи заглавными с трекингом .07em — это язык приборов, а не сайта.
- Глагол в повелительном наклонении: «Взять рейс», «Заправиться», «Сдать груз». Не «Подтвердить» и не «ОК».
- Иконка слева от текста и только если она добавляет смысл. Иконка ради украшения запрещена.
- Красная кнопка не бывает первой по порядку. Сначала безопасный выход, потом разрушительное действие.
14
Метки, клавиши, аватары
Мелкие кирпичи: статусы, счётчики, клавиши, водитель, карточка, прогресс, разделитель и заглушка. Всё рассчитано на тёмный фон и на то, что рядом может оказаться размытый кадр.
СТАТУСЫ
Цвет метки равен смыслу и совпадает с палитрой мира: янтарный — в работе, зелёный — сдано, красный — просрочено, циан — служебное.
ТОЧКА И СЧЁТЧИК
Точка — состояние без слова, светится своим цветом. Счётчик — только число, минимальная ширина держит круг круглым до 99.
КЛАВИШИ
Кейкап из игры, переложенный в DOM: лазурная обводка сверху, салатовая снизу, тень в два пикселя. Размер одинаковый на весь ряд.
ВОДИТЕЛЬ
Аватар — инициалы в квадрате со скруглением, не круг: круг в этом языке занят лампами. Стопка аватаров — смена на линии.
КАРТОЧКА
Шапка, тело, подвал. Шапка и подвал отделены линией, не фоном. Действия в подвале прижаты вправо.
ПРОГРЕСС И ШАГИ
Полоса — определённая, бесконечная и по шагам. Заливка янтарная, у успеха зелёная. Высота 7 px, рамка на месте даже при нулевом значении.
РАЗДЕЛИТЕЛЬ И ЗАГЛУШКА
Разделитель с подписью режет длинный список на смысловые части. Заглушка мерцает бегущим градиентом, повторяя форму будущего содержимого.
15
Поля и формы
Поле — это вдавленный прямоугольник: фон темнее панели, обводка в 28 % и подпись капсом сверху. Никаких плавающих подписей и никаких полей без рамки.
ТЕКСТОВОЕ ПОЛЕ
Подпись капсом над полем, помощь под ним. Фон темнее панели — поле «вдавлено». Фокус меняет цвет рамки на циан, не добавляя свечения.
МНОГОСТРОЧНОЕ И ЧИСЛО
Число — не поле ввода, а два шага и значение между ними: так не набрать «двенадцать тонн прописью».
ВЫБОР ИЗ СПИСКА
Короткий известный список — обычный select со стрелкой. Длинный или с поиском — выпадающее меню из раздела «Оверлеи».
ФЛАЖКИ И ПЕРЕКЛЮЧАТЕЛИ
Флажок квадратный, вариант — круглый, и перепутать их нельзя. Тумблер повторяет навигаторный: включённый светится янтарным.
ПОЛЗУНОК, КОД, ФАЙЛ
Ползунок — тонкая линия и кружок с ореолом. Код набирается по ячейкам, текущая подсвечена. Зона перетаскивания — пунктир, единственный в системе.
до 20 МБ
Правила
- Подпись всегда сверху и всегда капсом. Плейсхолдер вместо подписи запрещён: он исчезает при вводе.
- Ошибка под полем, красным, одной строкой и говорит, что сделать: «Нужен формат A-000», а не «Неверное значение».
- Единица измерения в подписи, а не в поле: «Груз, тонн» — и в поле чистое число.
- Заблокированное поле не исчезает, а гаснет. Пропадающие поля меняют высоту формы и сбивают с толку.
- Пунктир — только у зоны перетаскивания. Больше нигде в системе пунктирных рамок нет.
16
Данные и списки
Таблицы, пары «поле — значение», хронология, пустые состояния и разворачиваемые блоки. Разделяем линиями, а не подложками: подложка в этой системе означает выбор.
ТАБЛИЦА
Шапка капсом и приглушена, строки разделены линией в 70 % прозрачности. Выбранная строка — янтарная подложка в 6 %, без рамки.
| Рейс | Груз | Плечо | Ставка | Статус |
|---|---|---|---|---|
| № 148 | Лес 9 т | 1.2 км | £168 | В пути |
| № 147 | Уголь 14 т | 780 м | £196 | Сдано |
| № 146 | Нефть 11 т | 2.0 км | £244 | Просрочен |
ПОЛЕ — ЗНАЧЕНИЕ
Две колонки: название приглушено и капсом, значение светлое. Ширина первой колонки — по самому длинному названию, не по проценту.
- Тягач
- 540 л. с., 6×4
- Прицеп
- 13.6 м, тент
- Полная масса
- 32.6 т
- Тормозной путь
- 41 м с 50 км/ч
- Расход
- 34.8 л / 100 км
ХРОНОЛОГИЯ СМЕНЫ
Вертикальная линия и точки. Текущее событие — янтарная точка с ореолом, прошедшие — контур. Время мелко под заголовком.
- Загружено 12 т · лес
- Штраф −£40 · превышение
- Залито 150 л
- Сдано в депо · +£168
ПУСТОЕ СОСТОЯНИЕ
Иконка, одна строка, что произошло, и действие. Никаких извинений и «упс».
Подъезжайте к любой будке и снимите трубку.
РАЗВОРАЧИВАЕМЫЕ БЛОКИ
Плюс и минус вместо шеврона — знак совпадает с ремкомплектом и читается на любом кегле.
Почему прицеп складывается
Задним ходом угол между тягачом и прицепом растёт сам. Скорость ниже 3 км/ч, руль мелкими движениями, смотреть в зеркала.
Откуда берётся штраф
Пост ДПС меряет скорость в радиусе 78 метров. Предел — 30 миль в час, штраф списывается сразу.
Что считается повреждением
Съезд с дороги, удар о здание и столкновение с машиной. Повреждения кабины и прицепа считаются отдельно.
18
Оверлеи и уведомления
Всё, что всплывает поверх: окно, подсказка, поповер, меню действий, уведомление, сообщение на странице и объявление. Окно повторяет игровое — с уголками вместо рамки.
МОДАЛЬНОЕ ОКНО
Те же четыре циановых уголка, что в игре. Заголовок цветом раздела, кнопки внизу: сначала безопасный выход, потом действие.
Бросить рейс?
Груз вернётся поставщику, ставка £168 не начислится.
Штрафа не будет.
ПОДСКАЗКИ
Тултип — только текст, без кнопок внутри. Поповер — блок у элемента, без затемнения фона, в нём допустимы действия.
Радар мерит в радиусе 78 м. Предел 30 миль в час.
МЕНЮ ДЕЙСТВИЙ
Выпадающее меню у кнопки: иконка, подпись, клавиша справа. Разрушительный пункт красный и отделён линией.
УВЕДОМЛЕНИЯ
Всплывает после действия, живёт 1.2—3.5 секунды. Цвет говорит об исходе: зелёное — деньги пришли, красное — ушли, нейтральное — просто факт.
СООБЩЕНИЕ НА СТРАНИЦЕ
В отличие от уведомления не исчезает. Левая полоска в два пикселя цветом смысла, иконка того же цвета, заголовок светлый.
В баке 88 л из 400. Ближайшая заправка в 366 м по маршруту.
Ещё один удар — и груз придётся вернуть. Гараж в 1.4 км.
Премия £60 начислена. Следующий уровень откроется в конце смены.
После сигнала рейсы не засчитываются.
ОБЪЯВЛЕНИЕ
Полоса во всю ширину сверху страницы. Одна строка, одно действие, крестик справа.
19
Блоки страницы
Крупные блоки для лендинга и документации: шапка раздела, числа, фичи, списки ссылок и страница ошибки.
ШАПКА РАЗДЕЛА
Надзаголовок янтарным капсом, заголовок крупно, описание в одну мысль, дальше два действия: главное и второстепенное.
Juggernaut
Седельный тягач, ночной город и смена с восьми до шести. Всё нарисовано линиями на одном холсте.
ЧИСЛА
Крупное значение, под ним подпись капсом. Ровно то же, что в обзоре этого документа.
ФИЧИ
Иконка янтарная, заголовок светлый, описание приглушённое. Сетка сама подбирает число колонок.
ССЫЛКИ
Список с правой колонкой-пояснением. Разделители между строками, не рамки.
СТРАНИЦА ОШИБКИ
Код крупно янтарным, строка объяснения в духе проекта, одна кнопка возврата.
Такого адреса в городе нет.
Проверьте накладную или вернитесь в депо.
Правила
- Одна мысль на блок. Если описание не помещается в две строки, это два блока.
- Числа без украшений. Значение и подпись, никаких иконок рядом с цифрой.
- Иконка в фиче — янтарная, потому что она указывает; иконка в списке — приглушённая, потому что она метит.
- Текст ошибки говорит на языке проекта, а не «страница не найдена».
20
Карточки заставки
У заставки своё оформление — противоположное приборке: никакого стекла и градиентов, тонкая белая обводка и белый текст поверх фотографии. Шапка — белая плашка, из которой заголовок и клавиши вырезаны насквозь.
КАРТОЧКА
Шапка собирается в отдельном буфере: белая плашка, затем
destination-out выбивает текст и чипы, внутри чипа буква возвращается белой.
Готовый спрайт кешируется по ширине и подписи.
СТРОКА ВЫБОРА
Выбранный пункт — яркая обводка и треугольник, остальные приглушены до 0.18. Никаких заливок: выбор читается только контрастом линии.
cardRow()- Лента карточек прижата к низу и отцентрована; уже 700 px ломается на два ряда по три.
- Под лентой — еле заметное затемнение фотографии (до 0.38), только ради читаемости.
- Кадр заставки растворяется по бокам полосой 360 px по степенной кривой — резкой вертикальной границы быть не должно.
22
Мир и дорога
Дорога рисуется тремя разными штрихами, и путать их нельзя: борт — точки, осевая — штрихи, служебная разметка — тонкий пунктир.
ДОРОГА СВЕРХУ
Борт — точки с шагом ~4.4 px, фаза привязана к миру, поэтому соседние отрезки продолжают друг друга. Осевая — штрих 8 м через 8 м. Концы линий гаснут ступенями к перекрёстку.
drawTopView() · FADEAДОРОГА В ПЕРСПЕКТИВЕ
Точка не может быть коротким отрезком в мире — в перспективе он растянется в штрих. Каждая точка проецируется вручную и рисуется квадратиком в экранных координатах: ближе — крупнее и ярче.
edgeDots3D()Почему точки, а не линия
Тонкая косая линия в единицу толщины рвётся сглаживанием: пиксели набираются неравномерно, линия идёт муаром и выглядит как брак. Точка такого дефекта не имеет — поэтому весь «мелкий» слой мира набирается точками.
23
Текст в интерфейсе
- Две локали. Русский — исходный, английский — словарём.
ctx.fillTextобёрнут и переводит сам. - Переводить до подгонки. Обрезанная строка в словаре не находится: сначала
TR(), потом ужимание и многоточие. - Короткие слова — только точным словарём. Пословная подстановка «Л»→«L» превращает «ПЛАН» в «ПLАН».
- Числа собираются из переведённых кусков, а не переводятся целой строкой.
- Тон подписей — телеграфный: «ЗАБРАТЬ», «ОТВЕЗТИ», «ВРЕМЕНИ В ОБРЕЗ». Ни одного лишнего слова.
24
Чего не делать
Не ставить плоские заливки
Кружок вместо свечения, чёрный прямоугольник вместо прозрачности, серая плашка под клавишей — всё это читается рамкой и ломает язык линий.
Не рисовать две линии по одному месту
Белый борт и красный пунктир по той же кромке дали муар. Одна сущность — одна линия.
Не растворять в долях
Доля от ширины на широком элементе съедает десятки пикселей. Растворение и отступы — в пикселях.
Не уменьшать клавиши
Размер бейджа одинаковый на весь ряд. Не помещается — сдвигаем рейку, а не мельчим.
Не смешивать роли цветов
Оранжевый только цель, красный только опасность, салатовый только ремкомплект, голубой только заправка.
Не спорить по скриншоту
Если линия «резкая» или «не видна» —
читать пиксели getImageData, а не подбирать на глаз.
25
Токены
Все числа проекта в одном месте. Если значение встречается больше одного раза — оно здесь; если один раз и от геометрии — считается формулой на месте.
Сетка и размеры
| Токен | Значение | Где |
|---|---|---|
MPAD | 12 px | зазор между модулями и внутренний отступ панели |
MINSET | 4 px | отступ содержимого от рамки модуля |
| внутренний отступ модуля | 7–9 px | левый край подписи и содержимого |
CARDP | 11 px | горизонтальный отступ в карточке заставки |
| рейка клавиш | 16 px узко / 21 px обычно | высота ряда над панелью |
Радиусы
| Элемент | Радиус | Форма |
|---|---|---|
| модуль приборки | 7 px | суперэллипс |
| внутренняя рамка модуля | 5 px | суперэллипс |
| карточка заставки | 3 px | дуга |
| клавиша | 0.20 · h | суперэллипс |
| ячейка вида | 4 px | суперэллипс |
| чип клавиши в шапке карточки | 2.5 px | дуга |
Линии
| Роль | Толщина | Цвет |
|---|---|---|
| контур по умолчанию | 1 | dim / mid |
| иконка вида | 0.9 | mid, выбранная accent |
| метка мира | 1.2–1.3 | свой цвет |
| стрелка навигатора | 1.7 | accent, срочная bad |
| стрелка прибора | 1.8–2.2 | hi |
| внешняя рамка модуля | 1 (полпикселя со сдвигом) | edge |
Кегли
| Роль | Кегль | Вес |
|---|---|---|
| подпись модуля | 8.5 | 600 |
| название трека на радио | 6.5–8.2 | 500 |
| строка данных | 9–11 | 500–600 |
| значение | 13–16 | 700 |
| крупное показание | 20–34 | 800 |
| заголовок карточки заставки | 12.5, ужимается до 8.5 | 800 |
Свет и прозрачность
| Что | Значение |
|---|---|
| ореол сигнала | 5 стопов, радиус 2.0–3.0 от знака, режим lighter |
| ореол метки мира | альфа 0.15→0, пульсация от gameClock |
| ореол иконки интерфейса | та же кривая, альфа вдвое ниже |
| подложка выбранного пункта | 0.14 |
| неактивный пункт карточки | 0.18 |
| затемнение под лентой заставки | до 0.38 |
| растворение кадра по бокам | 360 px, 14 стопов по степенной кривой |
Дорога
| Штрих | Параметры | Смысл |
|---|---|---|
| борт сверху | точки, шаг ≈4.4 px, фаза от мира | край проезжей части |
| борт в перспективе | квадратик clamp(FOCAL·0.22/d, 0.55, 2.4), 6 ступеней альфы | тот же край |
| осевая | штрих 8 м через 8 м | разделение потоков |
| служебная разметка | тонкий пунктир | подъезды, доки |
| затухание у перекрёстка | FADEN = 7 ступеней | концы линий не упираются в узел |
26
Как пользоваться
Порядок при новой задаче
- Найти прецедент. Почти всё уже нарисовано: рамка, клавиша, метка, стрелка, прибор. Новый элемент собирается из существующих примитивов, а не рисуется с нуля.
- Взять число из раздела 15. Если нужного нет — считать формулой от геометрии блока, а не вписывать константу.
- Проверить по разделу 14. Шесть запретов закрывают большинство ошибок, которые уже случались в проекте.
- Смотреть пиксели, а не глазами. Спор «резко / не видно» решается
getImageDataна скриншоте, а не подбором на глаз.
Где что лежит
| Вопрос | Где ответ |
|---|---|
| Как это выглядит и почему | этот документ |
| Как устроен код, физика, деплой | PROJECT.md рядом |
| Как это нарисовано | исходник: имена функций подписаны под каждым образцом |
Об образцах
Каждый образец на этой странице — отдельная канва, в которую скопирован рабочий код
из игры: rr(), seQuarter(), modFrame(),
keyBadge(), keyRail(), navSw(),
gaugeDial(), pumpIcon(), viewIcon(),
hdrSprite(). Если правило в игре меняется — образец надо обновить здесь же,
иначе документ начинает врать.