За последние 2 месяца был конкурс часов Flutter Clock Challenge, и это было как раз в моем переулке. Я люблю программировать графику и люблю флаттер. Часы - отличный способ создать красивый пользовательский интерфейс, потому что вся анимация - это просто функция движения во времени.

Моя запись оказалась часами времени и пространства (Github). Я настоятельно рекомендую людям ознакомиться с ним, он был закодирован и задокументирован, чтобы быть ясным и лаконичным в реализации. По мере прохождения этой статьи я буду ссылаться на соответствующие разделы в github, чтобы помочь людям разобраться в коде.

Сами часы - это действительно 2 часа, цифровые часы и аналоговые часы. Цифровые часы - это текстовое представление, плавающее в верхнем левом углу, созданное с помощью системы и композиции виджетов. Аналоговые часы представлены положением Солнца (час), Земли (минуты) и Луны (секунды).

Разбивка статьи выглядит следующим образом

  1. Общие утилиты / Foundation
  2. Цифровые часы (верхний левый тикер)
  3. Космические часы (Звезды, Солнце, Земля и Луна)

Помощники / Утилиты

Определенные компоненты были основой, и я сначала расскажу о них. Эти компоненты могут быть легко использованы в других проектах.

Тикер (github)

Чтобы построить это, сначала я создал общий виджет под названием «Ticker». Тикер выполняет несколько функций.

StringBuilder () = ›Строка, которую мы хотим показать в Ticker
WidgetDigitBuilder (value, first, last) =› Создает виджет для каждой цифры

Внутри тикер будет строить строку каждые 1 с, затем он будет перестраивать виджеты и неявно анимировать их по мере изменения строки.

Для этого используется Animated Switcher (Flutter Docs) для каждого символа и ValueKey, чтобы помочь указать виджету, когда следует анимировать.

AnimatedPaint

Адаптер для CustomPaint для облегчения анимации. Рисование обрабатывается интерфейсом AnimatedPainter. AnimatedPainter получает вызов init (), который можно использовать для загрузки ресурсов, он также получает метод рисования (холст, размер), который будет вызываться при обновлении экрана, так что 120 Гц для этого не должно быть проблемой.

Методы расширения (github)

При его создании используются различные методы расширения. Я расширяю Double, ClockModel, TextStyle, String, Ui.Image и Vector для различных параметров, используемых круглосуточно, и различных компонентов.

Следует отметить Цепочку (github), которую я использую для построения функциональных цепочек. При использовании флаттера мне нравится избегать методов build () с телами блоков, когда это возможно. Chain () в значительной степени похож на Map () и позволяет мне функционально масштабировать значение, и при этом я могу свернуть тела блоков в указатели выражений. Например.

build (BuildContext context) {
final text = getText ();
return Text (text);
}

может стать

build (BuildContext context) = ›getText (). chain ((text) =› Text (текст))

Я не назвал его map (), потому что это универсальный метод расширения для всех типов, и карта могла бы конфликтовать.

Поскольку я фанат функционального программирования, я сделал выбор, чтобы сделать код местами немного более функциональным.

Для более полного изучения методов расширения я рекомендую просмотреть файлы в GitHub, чтобы узнать, что они все делают. Там все задокументировано.

Цифровые часы

Целью цифровых часов было отображение сжатой информации на небольшой части экрана с небольшим стилем. Я хотел, чтобы он представлял «тикер», где цифры могут обновляться независимо друг от друга с некоторой анимацией.

TickerClock

Мы используем общую утилиту Ticker, описанную выше, виджет похож на абстрактный класс, который мне все еще нужен как часть реализации. Это делается в TickerClock (github).

TickerClock выполняет следующие обязанности

  • Создайте строку для отображения в тикере
  • Создайте виджеты того, как мы будем отображать каждого персонажа
  • Настройте фон для всего тикера

Для этого в верхней части файла есть набор функций, которые могут создавать и форматировать дату, а также генерировать строку, которую я хотел бы отобразить вверху.

Генерация строки предназначена для переключения вторичной информации каждые 5 секунд. Это такая информация, как (дата / погода / место), в то время как время постоянно отображается слева.

Генерация пользовательского интерфейса довольно проста. Каждый символ отображается в контейнере с фиксированной высотой, а затем он центрируется в шрифте, который я выбрал для этого (Nova Mono).

Фон делается с помощью тени BoxDecoration + радиус границы. Это делает его похожим на размытый контейнер с закругленными краями. Цвет тени берется из текущей темы и переключается между черным и белым.

Аналоговые часы

Здесь большая часть часов нарисована на холсте. На высоком уровне он нарисован на холсте. Это выполняется с помощью помощника под названием AnimatedPaint (github), который представляет собой абстракцию, которая добавляет обратный вызов init () для загрузки изображений и draw (Canvas, Size) это будет вызвано как можно быстрее. Это позволяет мне работать на 60/120 или любой другой флаттер обновления может вызвать это.

Конфигурация пространства (github)

Этот файл является конфигурацией Space Clock. Он предоставляет значения, с помощью которых можно изменять внешний вид. Есть светлые и темные конфиги, в которых солнце большое или маленькое, соответственно на ваш выбор.

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

SpaceClockScene (github)

Это реализация AnimatedPainter, используемая для рисования сцены. Функция init () загружает изображения, используемые асинхронно, в карту. Они сохраняются в глобальном масштабе, так как я могу перестроить рисовальщик при изменении конфигурации, но я не хочу повторно загружать изображения.

Paint () будет показывать индикатор выполнения на этом этапе, но это не займет много времени.

Само рисование выполняется с помощью алгоритма Painters (Back to Front) (github).

  1. Нарисуйте фон
  2. Нарисуйте звезды
  3. Нарисуйте солнце
  4. Нарисуйте Землю / Луну (порядок переворачивания зависит от того, идет ли Луна позади или впереди планеты)

Там, где он рисует, все рассчитывается в SpaceViewModel (github)

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

  1. Преобразование времени в радианы с высокой точностью (например, угол руки)
  2. С помощью Config рассчитайте положение, размеры и поворот элементов.
  3. Верните ViewModel для AnimatedPainter, чтобы использовать

Фон

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

Звезды (github)

Звездочки находятся в собственном файле, они предоставляют один метод drawStars (холст, размер, вращение, время), и он будет делать именно это.

Все состояние фиксировано и статично. Звезды - это просто набор неподвижных точек в космосе. Они «перемещаются» по оси z со временем, и я отсекаю часть позиции, позволяя им «зацикливаться» спереди назад. Со временем меняется и видимое положение звезды по оси Z.

Рисование большого количества частиц может привести к большому количеству вызовов отрисовки, что может повлиять на производительность. Чтобы смягчить этот эффект, звезды рисуются партиями в зависимости от их цвета (они исчезают на расстоянии). Это позволяет мне рисовать N звезд с фиксированным количеством вызовов отрисовки, что дает довольно хороший коэффициент масштабирования производительности.

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

Солнце

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

Это делается с помощью техники, называемой шумом Перлина, где несколько шумных изображений объединяются для создания эффекта типа дым и облака. это обычный эффект в графическом программировании. Это также фильтр облака в фотошопе. Конфиг для слоев и смешивания хранится в конфиге Space (github). Я добавил возможность переворачивать слои, чтобы одно изображение могло действовать как два слоя без заметного визуального перекрытия.

В псевдо-коде отрисовка солнца выглядела так (github)

  1. Нарисуйте основной диск солнца (с градиентом, который постепенно исчезает по краям и становится красным, для визуального эффекта)
  2. Нарисуйте слои шума Перлина солнца с соответствующим растровым изображением / режимом наложения / скоростью вращения

И на самом деле это все, что касается солнца. Это проще, чем кажется, но я действительно горжусь тем, как это выглядит. Это похоже на пылающий шар газа, который постоянно меняется, с короной, случайными солнечными пятнами и всем остальным. На самом деле это просто пара изображений, вращающихся друг над другом.

Земля и Луна

Даже проще, чем солнце, мы просто рисуем эти изображения, а затем накладываем запеченную тень поверх изображения (повернутого в противоположную сторону от времени).

Луна проходит за Землей на 45–15 секунд секундной стрелки, поэтому мы учитываем это в порядке прорисовки.

Заключение

Вот и все. В часах нет ничего ужасного, они разработаны, чтобы быть краткими и легко настраиваемыми / перенастраиваемыми. Скорее всего, когда закончится конкурсный период, я опубликую его в виде двух библиотек. Один предлагает общие компоненты и методы расширения, используемые для людей, которым нужен только AnimatedPaint или Ticker, а другой - с ресурсами, позволяющими виджеты звездного поля, солнца, земли, луны и т. Д., Которые люди могут использовать в своих собственных приложениях.

Спасибо, что нашли время, чтобы прочитать это, и я надеюсь, что это дает ценную информацию о разработке флаттера.