JG

JUGGERNAUT — ДИЗАЙН-СИСТЕМА

CRL / Pete Cooke, 1985 · canvas 2D · справочник
Чертёж · тонкая линия · JetBrains Mono

01

Обзор

Juggernaut — браузерная реконструкция игры CRL / Pete Cooke (1985): один HTML-файл, всё рисуется на canvas 2D. Этот документ собирает визуальный язык проекта в одном месте — основы, цвет, геометрию, свет, компоненты и правила. Все образцы ниже живые: они рисуются тем же кодом, что и игра, а не вставлены картинками.

26разделовосновы, компоненты, правила
13цветовых токеновобъект C в исходнике, каждый закреплён за смыслом
10иконок и метокединый контурный набор
12модулей приборкиот «ВИД» до «ЗВУК», все живые ниже
314функций8 689 строк одного файла
60+живых образцовcanvas из игры плюс веб-кит на DOM

Из чего состоит

ФайлЧто внутриРоль
juggernaut.htmlмир, физика, все виды, интерфейс, радиоединственный исходник, правится только он
wrap.pydoctype, 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)
РольВесКегльЦвет
Подпись модуля (ВИД, ГРУЗ)6008.5mid
Строка данных500–6009–11mid / hi
Значение70013–16hi
Крупное показание (часы, частота)80020–34hi / accent
Заголовок карточки заставки80012.5 (ужимается)ink на белом
  • Капс везде. Интерфейсные подписи — заглавными, это язык приборов.
  • Кегль от геометрии: clamp(Math.min(w*0.3, h*0.5), min, max) — текст переживает любую ширину модуля.
  • Длинное имя не режется, а ужимается до предела, и только потом fitTxt() ставит многоточие.
  • Выравнивание возвращать. После textAlign='right' обязательно вернуть 'left' и textBaseline='alphabetic' — иначе поедет соседний модуль.

05

Геометрия и ритм

Скругления — не дуги, а суперэллипс (squircle): кривизна непрерывна, угол читается объёмным. Один радиус на всю приборку, один отступ на всю сетку.

SQUIRCLE ПРОТИВ ДУГИ

Слева обычная дуга, справа суперэллипс — тот же радиус, но угол кажется толще и мягче. В проекте используется только правый.

rr() · seQuarter()
ВеличинаЗначениеГде
MPAD12 pxзазор между модулями и внутренний отступ — одно число
MINSET4 pxотступ содержимого от рамки модуля
Радиус модуля7 px (не больше w/2, h/2)modFrame
Радиус карточки заставки3 pxcardShell
Радиус клавиши0.20·hkeyBadge
Толщина линии1 px, акцент 1.6–2.2везде

06

Свет

Источники света не рисуются кружком. Только мягкий ореол радиальным градиентом в режиме lighter, с тоном теплее лампы.

ОРЕОЛ СИГНАЛА

Пять стопов с быстрым спадом, пульсация от игрового времени. Красный, жёлтый, зелёный — у каждого свой тёплый ореол.

glowSprite() · lighter

07

Размытие без 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.

Смена идётБак почти пуст31299+
КЛАВИШИ

Кейкап из игры, переложенный в DOM: лазурная обводка сверху, салатовая снизу, тень в два пикселя. Размер одинаковый на весь ряд.

EPFM[]Shift
ВОДИТЕЛЬ

Аватар — инициалы в квадрате со скруглением, не круг: круг в этом языке занят лампами. Стопка аватаров — смена на линии.

АГ
Артём Г.Смена 08:00 — 18:00 · рейсов 7
АГ
ПК
МД
+4
КАРТОЧКА

Шапка, тело, подвал. Шапка и подвал отделены линией, не фоном. Действия в подвале прижаты вправо.

Рейс № 148В пути
Лес, 9 т. Лесопилка → Депо. 1.2 км, ставка £168. Осталось 4 ч 12 мин до конца смены.
ПРОГРЕСС И ШАГИ

Полоса — определённая, бесконечная и по шагам. Заливка янтарная, у успеха зелёная. Высота 7 px, рамка на месте даже при нулевом значении.

Погрузка 12 т · лес — 62 %
План «Уголь» выполнен
Связь с диспетчерской…
Уровень 3 из 5
РАЗДЕЛИТЕЛЬ И ЗАГЛУШКА

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

Смена 14 сентября

15

Поля и формы

Поле — это вдавленный прямоугольник: фон темнее панели, обводка в 28 % и подпись капсом сверху. Никаких плавающих подписей и никаких полей без рамки.

ТЕКСТОВОЕ ПОЛЕ

Подпись капсом над полем, помощь под ним. Фон темнее панели — поле «вдавлено». Фокус меняет цвет рамки на циан, не добавляя свечения.

МНОГОСТРОЧНОЕ И ЧИСЛО

Число — не поле ввода, а два шага и значение между ними: так не набрать «двенадцать тонн прописью».

Груз, тонн12
ВЫБОР ИЗ СПИСКА

Короткий известный список — обычный select со стрелкой. Длинный или с поиском — выпадающее меню из раздела «Оверлеи».

ФЛАЖКИ И ПЕРЕКЛЮЧАТЕЛИ

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

Вид по умолчанию
Звуковые эффекты
Трафик на дорогах
ПОЛЗУНОК, КОД, ФАЙЛ

Ползунок — тонкая линия и кружок с ореолом. Код набирается по ячейкам, текущая подсвечена. Зона перетаскивания — пунктир, единственный в системе.

Длина прицепа — 13.6 м
Код от ворот депо
481
Перетащите карту городаOSM или GeoJSON, только дороги и здания
до 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
ПУСТОЕ СОСТОЯНИЕ

Иконка, одна строка, что произошло, и действие. Никаких извинений и «упс».

Заказов нетДиспетчерская пуста до 09:00.
Подъезжайте к любой будке и снимите трубку.
РАЗВОРАЧИВАЕМЫЕ БЛОКИ

Плюс и минус вместо шеврона — знак совпадает с ремкомплектом и читается на любом кегле.

Почему прицеп складывается

Задним ходом угол между тягачом и прицепом растёт сам. Скорость ниже 3 км/ч, руль мелкими движениями, смотреть в зеркала.

Откуда берётся штраф

Пост ДПС меряет скорость в радиусе 78 метров. Предел — 30 миль в час, штраф списывается сразу.

Что считается повреждением

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

17

Навигация

Вкладки, крошки, страницы, шаги, боковое меню и поиск по командам. Активное состояние всегда янтарное, и только одно на экран.

ВКЛАДКИ

Активная — янтарная подпись и полоска снизу, которая лежит на общей линии, а не над ней. Подложки у вкладок нет.

ХЛЕБНЫЕ КРОШКИ

Разделитель — слэш, не стрелка: стрелка в этой системе занята навигатором.

ПАГИНАЦИЯ

Квадраты со стороной 28. Текущая страница янтарная с подложкой, стрелки — такие же квадраты, без подписи.

ШАГИ МАСТЕРА

Пройденные — зелёная точка, текущий — янтарная с ореолом, будущие — контур. Линия между шагами не доходит до кружка.

Тягач
Прицеп
Груз
Маршрут
БОКОВОЕ МЕНЮ

Иконка, подпись, счётчик справа. Активный пункт — янтарная подложка в 10 %, без левой полоски: полоска занята оглавлением документа.

ПОИСК ПО КОМАНДАМ

Строка поиска и подсказка клавиши. Открывается по Ctrl + K, ищет команды, а не текст: «залить», «карта», «сменить вид».

K

18

Оверлеи и уведомления

Всё, что всплывает поверх: окно, подсказка, поповер, меню действий, уведомление, сообщение на странице и объявление. Окно повторяет игровое — с уголками вместо рамки.

МОДАЛЬНОЕ ОКНО

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

Бросить рейс?

Груз вернётся поставщику, ставка £168 не начислится.
Штрафа не будет.

ПОДСКАЗКИ

Тултип — только текст, без кнопок внутри. Поповер — блок у элемента, без затемнения фона, в нём допустимы действия.

Ctrl + K — командыТормозной путь 41 м
Пост ДПС
Радар мерит в радиусе 78 м. Предел 30 миль в час.
МЕНЮ ДЕЙСТВИЙ

Выпадающее меню у кнопки: иконка, подпись, клавиша справа. Разрушительный пункт красный и отделён линией.

УВЕДОМЛЕНИЯ

Всплывает после действия, живёт 1.2—3.5 секунды. Цвет говорит об исходе: зелёное — деньги пришли, красное — ушли, нейтральное — просто факт.

Сдано в депо · +£168
Штраф −£40 · превышение
Волна 95.0 FM · Underpass Dub
СООБЩЕНИЕ НА СТРАНИЦЕ

В отличие от уведомления не исчезает. Левая полоска в два пикселя цветом смысла, иконка того же цвета, заголовок светлый.

Мало топлива

В баке 88 л из 400. Ближайшая заправка в 366 м по маршруту.

Прицеп повреждён на 74 %

Ещё один удар — и груз придётся вернуть. Гараж в 1.4 км.

План «Уголь» выполнен

Премия £60 начислена. Следующий уровень откроется в конце смены.

Смена заканчивается в 18:00

После сигнала рейсы не засчитываются.

ОБЪЯВЛЕНИЕ

Полоса во всю ширину сверху страницы. Одна строка, одно действие, крестик справа.

Новая карта: ночной Манчестер, 14 перекрёстков✕

19

Блоки страницы

Крупные блоки для лендинга и документации: шапка раздела, числа, фичи, списки ссылок и страница ошибки.

ШАПКА РАЗДЕЛА

Надзаголовок янтарным капсом, заголовок крупно, описание в одну мысль, дальше два действия: главное и второстепенное.

CRL · Pete Cooke · 1985
Juggernaut

Седельный тягач, ночной город и смена с восьми до шести. Всё нарисовано линиями на одном холсте.

ЧИСЛА

Крупное значение, под ним подпись капсом. Ровно то же, что в обзоре этого документа.

32.6 тполная масса
41 мтормозной путь
34.8л / 100 км
15радиоволн
ФИЧИ

Иконка янтарная, заголовок светлый, описание приглушённое. Сетка сама подбирает число колонок.

Настоящая физикаМасса, мощность и тормоза считаются в ньютонах
Четыре видаСверху, изометрия, кабина и камера сзади
Реальный расходОт тяги и массы, а не по таймеру
ССЫЛКИ

Список с правой колонкой-пояснением. Разделители между строками, не рамки.

СТРАНИЦА ОШИБКИ

Код крупно янтарным, строка объяснения в духе проекта, одна кнопка возврата.

404

Такого адреса в городе нет.
Проверьте накладную или вернитесь в депо.

Правила

  • Одна мысль на блок. Если описание не помещается в две строки, это два блока.
  • Числа без украшений. Значение и подпись, никаких иконок рядом с цифрой.
  • Иконка в фиче — янтарная, потому что она указывает; иконка в списке — приглушённая, потому что она метит.
  • Текст ошибки говорит на языке проекта, а не «страница не найдена».

20

Карточки заставки

У заставки своё оформление — противоположное приборке: никакого стекла и градиентов, тонкая белая обводка и белый текст поверх фотографии. Шапка — белая плашка, из которой заголовок и клавиши вырезаны насквозь.

КАРТОЧКА

Шапка собирается в отдельном буфере: белая плашка, затем destination-out выбивает текст и чипы, внутри чипа буква возвращается белой. Готовый спрайт кешируется по ширине и подписи.

cardShell() · hdrSprite()

СТРОКА ВЫБОРА

Выбранный пункт — яркая обводка и треугольник, остальные приглушены до 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

Токены

Все числа проекта в одном месте. Если значение встречается больше одного раза — оно здесь; если один раз и от геометрии — считается формулой на месте.

Сетка и размеры

ТокенЗначениеГде
MPAD12 pxзазор между модулями и внутренний отступ панели
MINSET4 pxотступ содержимого от рамки модуля
внутренний отступ модуля7–9 pxлевый край подписи и содержимого
CARDP11 pxгоризонтальный отступ в карточке заставки
рейка клавиш16 px узко / 21 px обычновысота ряда над панелью

Радиусы

ЭлементРадиусФорма
модуль приборки7 pxсуперэллипс
внутренняя рамка модуля5 pxсуперэллипс
карточка заставки3 pxдуга
клавиша0.20 · hсуперэллипс
ячейка вида4 pxсуперэллипс
чип клавиши в шапке карточки2.5 pxдуга

Линии

РольТолщинаЦвет
контур по умолчанию1dim / mid
иконка вида0.9mid, выбранная accent
метка мира1.2–1.3свой цвет
стрелка навигатора1.7accent, срочная bad
стрелка прибора1.8–2.2hi
внешняя рамка модуля1 (полпикселя со сдвигом)edge

Кегли

РольКегльВес
подпись модуля8.5600
название трека на радио6.5–8.2500
строка данных9–11500–600
значение13–16700
крупное показание20–34800
заголовок карточки заставки12.5, ужимается до 8.5800

Свет и прозрачность

ЧтоЗначение
ореол сигнала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(). Если правило в игре меняется — образец надо обновить здесь же, иначе документ начинает врать.