По любви · Android × Figma · 11.09.2026
Десять экранов второй половины приложения — от экрана «подписка оформлена» до контекстного меню сообщения — собраны из данных нод: переменных, стилей, матриц поворота, контуров форм и эффектов. Каждый кадр снят на устройстве в честном масштабе ×3 (1170×2532) и сведён с рендером Figma; кадры выше экрана — по частям с прокруткой. Ниже кадр рядом с рендером, четыре числа на кадр и разбор того, что оказалось не так. Главная находка партии — размытие фотографии размывает и её прозрачность: край карточки становится полупрозрачным, сквозь него видно фон, и поточечной подгонкой цвета это было не закрыть. Вторая по важности не про вёрстку, а про меру: число, по которому мы закрываем кадр, не видит блок, которого нет вовсе. Это продолжение артефактов по кадрам S01–S10, S11–S20, S21–S30 и S31–S40.
Смотровая
Тяните шторку, чтобы совместить макет и приложение. «Карта» показывает, где остаётся расхождение по геометрии: синим закрыты системный статус-бар, полоска home, клавиатура и диалоги разрешений iOS — их рисует Android, они в сравнении не участвуют (спека 6.4).
Числа
Полное — доля пикселей, отличающихся сильнее ΔE 6 вне масок. Без текста — то же с исключёнными текстовыми блоками. ±1 px — пиксель считается расхождением, только если не совпал ни с одним пикселем рендера в окне ±1: сдвиг растра на долю пикселя не есть ошибка вёрстки. Геометрия — два последних вместе, то есть чистая форма, положение и материал; по ней и выносится вердикт. Шкала показывает геометрию относительно порога 0,3 %.
| Экран | Кадр | Состояние | Полное % | Без текста % | ±1 px % | Геометрия % | Шкала до порога 0,3 % |
|---|
Разбор
Каждая строка — расхождение из реестра ловушек спеки: что показала нода, что делало приложение и сколько это стоило в числах.
На кадрах «нет подписки» фотография размыта радиусом 50. Эффект стоит не на прямоугольной обрезке, а на самом векторе формы — значит размывается уже обрезанный формой слой вместе с его альфой, а начисто край режет маска-близнец уже после размытия. У кромки альфа падает, сквозь неё виден белый фон — и рендер там светлее самой фотографии: в точке (60, 608) он 162,160,157 при фотографии 23,21,24. Размытая прямоугольная обрезка давала там 26,23,20, и никакой подбор цвета этого не закрывал. Теперь растр готовится в RGBA: маска формы, размытие в помноженном на альфу виде, обрезка формой в Compose.
Плашка имени на карточке симпатии — HUG по содержимому, и её габарит в ответе API
посчитан по старой ширине: 216 при настоящих 238…246. Плашка растёт в ту сторону, куда её
не прижали, и вместе с ней уезжает точка поворота — на Δ·(cos, sin). Какой край
габарита при этом неподвижен, зависит от знака угла: при +5° правый и нижний, при
−5° правый и верхний. Прежняя привязка «верх габарита» держала только второй случай:
у кадра 8425:23698 рендер рисует плашку на 2,7 dp выше числа ноды.
У пятой брендовой формы контур кончается на 278 из 280 единиц вьюпорта: габарит вектора
366,01×413,62 при карточке 366,01×416,60. scaleMode = FILL кроет
именно вектор, поэтому фотография в такой карточке на 0,7 % мельче остальных.
Растянутая на всю карточку, она уезжает вниз тем сильнее, чем ниже смотреть: +1 px на
300 dp глубины и +8 px на 600.
Под меню сообщения у ноды стоит BACKGROUND_BLUR: размывается всё, что под
ним, а не отдельная подложка. Modifier.blur на эмуляторе молчит, поэтому экран
снимается вторым кадром без меню, размывается на приборе с σ = радиус × 0,410
в пикселях снимка (×3) и кладётся картинкой. Там же выяснилось, что затемнение рисует
сам прямоугольник размытия, а не шторка поверх: вторая тень делала подложку втрое темнее —
16 единиц вместо 102 на белом.
Compose держит URL одним словом и переносит его целиком, а Figma рвёт после косой, дефиса и вопроса — но не внутри :// и не после =. В текст ставятся нулевые пробелы ровно в этих местах: чернила по кадру сошлись с −15 % до +0,03 %. Правило выведено на четырёх кадрах со ссылками и проверено на пятом, где ссылка процент-кодирована.
У половины пузырей размер в ответе API остался размером мастера — 141×44 или 159×44, — потому что авто-лейаут не пересчитан. Рамка считается по правилу самого авто-лейаута там, где ответ её не пересчитал, и берётся из ноды там, где пересчитал; правило проверено на пяти кадрах с посчитанной рамкой. Заодно из-за этого маска текста в мере вставала не на своё место — настоящие рамки теперь пишутся отдельным файлом и читаются и мерой расхождений, и мерой текста.
Два сердца пунктиром на пустом экране чатов выходили вдвое толще: обводить
strokeGeometry пунктира нельзя — это уже готовый контур штрихов, и обводка его
удваивает. Штрихи режутся по средней линии из vectorNetwork и
dashPattern: чернила 2037 dp² против 713 стали 713.
Первый — имя 18/22, второй — 18/24, третий — 18/24 в две строки (48) и с прижатым к нижней строке возрастом. Высоту самой плашки держит возраст, поэтому ошибка в межстрочном интервале имени по высоте плашки не видна — только по месту строк.
Чёрная заливка 35 % стоит только у первой карточки кадра; у трёх остальных того же кадра и у всех четырёх соседнего её нет. Правило «есть размытие — значит есть затемнение» красило карточки на 38 единиц канала темнее. Признак берётся из ноды: полупрозрачный вектор со сплошной заливкой во всю карточку — у силуэта-заглушки свои полупрозрачные эллипсы той же кисти, и они меньше карточки.
Под кнопкой «Узнай, кто поставил лайк» стояла белая полоса — в ноде у этого блока заливки нет вовсе. На снимке ровно с 689 dp начиналась белизна, а в рендере там видна размытая фотография и над кнопкой, и под ней.
Мера
Число, по которому мы закрываем кадр, считает форму и материал, но исключает текстовые блоки — иначе в него попадала бы разница отрисовки букв. Значит, ошибку в самом тексте оно не поймает. В этой партии из-за этого дважды стоял зелёный вердикт на кадре с настоящей ошибкой. Обе нашла отдельная мера — плотность чернил по каждому блоку.
Заголовок секции «Записки» с числом искался в раскладке по списку guid, и список промахнулся мимо двух кадров: на них заголовка просто не рисовалось. Метрика молчала — текст в ней замаскирован. Мера чернил показала у блока ноль при полных в рендере и −13,3 % по кадру. Узлы теперь ищутся по признаку, а не по списку.
Найдено уже после того, как десяток был закрыт, — той же мерой при повторной проверке. У непрочитанной строки текст сообщения чёрный и набран Manrope 16/20 с треком 0,2, а у прочитанных — Beigeish Black 14/18 с треком 0,1. Строка от этого шире на 30 dp. Геометрия кадра — 0,205 %, зелёная.
Высокие кадры снимаются частями с прокруткой. Если части не записать, нижняя полоса кадра не сверяется ни разу — и об этом ничто не сообщает. Так у кадра «no subscription» в 3140 dp не было снято ни одного кадра, а у «skeleton» в 1082 не проверялись нижние 238. Теперь есть проверка: пройтись по рендерам выше экрана и убедиться, что для каждого есть снимок с прокруткой не меньше «высота − 844».
Карты кадров складываются, и побеждает поздняя. Нашлось три случая: кадр «pagination» собирался и как симпатии, и как записки; кадр анкеты со всеми частями был записан дважды подряд; четыре кадра состояний генератор писал в один файл, а рисовал другой — из-за этого часть высокого кадра уехала к заготовке без полос скелета. Теперь есть проверка на повторы ключей по всем файлам раскладки.
Собрано
Кроме состояний уже готовых экранов, в этой партии впервые собраны семь больших экранов целиком — из нод, а не с картинки.
Шапка пары с фотографией в повёрнутой рамке-зеркале, пузыри с хвостами и цитатами, разделители дат, кружки догрузки сверху и снизу, панель ответа и правки, ссылки с подчёркиванием своим цветом, контекстное меню поверх размытого экрана, снеки. Один файл закрывает сразу два реестра — переписку и контекстное меню.
Пять карточек, у каждой своя брендовая форма, свой поворот 5…15° и своя плашка имени — тоже повёрнутая и тоже по содержимому. Плюс карусель записок из двух плашек под углом и белой карточки, шесть состояний прокрутки, скелет, два пустых состояния, ошибка и два кадра с размытыми фотографиями.
Карусель взаимных симпатий с формами, строки переписок со счётчиками, значками «проверен» и «подписка», галочками «прочитано» и состоянием «Печатает» из трёх гаснущих точек; скелеты, пагинация, снек ошибки и плашка правил сообщества. Рядом — список пар V3 с теми же формами.
Три шторки поверх пейволла и три тёмных экрана «подписка оформлена» с фоном, посчитанным из ноды; настройки с ячейками продления и экран подписки в профиле; список записок с карточками загрузки и снеком ошибки.
Вопросы
Здесь не ошибки вёрстки, а места, где макет спорит сам с собой или с собственным рендером. Мы делаем как в рендере и передаём пару дизайнеру.
| Что | Где | Как сейчас |
|---|---|---|
| Число записок в заголовке | 8090:17432 |
на трёх кадрах чёрное, на шести — Beigeish Gray; блок и кегль те же |
| Затемнение поверх размытой фотографии | 8555:43502 |
стоит у первой карточки кадра и отсутствует у трёх остальных и у всех четырёх соседнего кадра |
| Контур пятой брендовой формы | 2788:7837 |
кончается на 278 из 280 единиц вьюпорта — фотография в такой карточке на 0,7 % мельче, чем в остальных четырёх |
| Заголовок секции «Записки» | 8154:3994 · 13944:1830 |
на одном кадре лежит на y=170, на другом — на 190 при одинаковой шапке |
| Кадр V4 «scroll» | 13353:33007 |
в ноде стоит прозрачность 0,01 (приглушённая копия на странице), и рендер выгружен с альфой 3/255 — сравнивать нечем |
| Карточка контекстного меню | 16983:52863 |
скруглений в ноде нет вовсе, а рендер рисует скруглённую карточку |
| Строка списка пар V3 | 11404:26427 |
блок имени 241 dp во всю рамку, и значок «проверен» на x=314 попадает внутрь него |
| Блок текста поля ввода | 7647:27244 |
стоит на y=710 при поле 698…802, а рендер прижимает текст к низу поля |
Границы
Форма зелёная — это половина работы. Вот честный остаток.
| Что | Почему | Что нужно |
|---|---|---|
Кадр 13353:33007 — 8,61 % |
единственный кадр десятка, форма которого не доказана числом: в ноде стоит прозрачность 0,01, и рендер ×3 выгружен с альфой 3/255 — в нём 34 значения канала на весь кадр | перевыгрузить кадр из Figma без приглушения; пока сверен глазами по цветовому каналу |
| Поведение всех десяти экранов | трасса запросов против карт Flutter не снята ни по одному экрану | файловый лог запросов в debug-сборке и инструмент разбора |
| Два текстовых остатка | на одном кадре процент-кодированная ссылка переносится на знак раньше рендера (−32,9 % чернил), на другом текст в поле правки виден на строку короче (−10,4 %); геометрия у обоих зелёная | разобрать метрики шрифта и обрезку блока полем ввода |
| Экраны в приложении | все семь собраны и снимаются раскладкой, но в навигацию не включены | заменить прежние экраны и переснять кадры |
| Сплошная проверка чернил по блокам | именно она нашла две ошибки при зелёной геометрии — и прогнана пока точечно, а не по всем 356 кадрам проекта | прогнать по всему проекту; вердикт хуже 10 % сейчас у пяти кадров, из них два вне этого десятка не разбирались |
| Низ четырёх высоких кадров прошлых десятков | у них не записаны части прокрутки — нижняя полоса не сверялась ни разу | добавить части и переснять: 1349:13058, 1601:18880,
2998:14491, 3342:8104 |