Спойлер: весь код внутри этой статьи может работать как есть прямо в вашем браузере (без каких-либо сборок/транспиляций). По умолчанию он работает даже внутри WebWorker.

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

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

Содержание

  1. Демонстрационное видео
  2. Проблема в деталях
  3. Как использовать анимированные списки в своих приложениях?
  4. Требование: реализация коллекции (хранилища данных)
  5. Погружение в list.plugin.Animate
  6. Код и онлайн-демонстрации
  7. Идеи улучшения
  8. Обучение neo.mjs
  9. Accenture нанимает разработчиков neo.mjs в Германии

1. Демонстрационное видео

Видео финальной реализации можно посмотреть здесь:

2. Проблема в деталях

Чтобы преобразовать представление данных в анимированный список, нам нужно поддерживать 3 операции:

  1. Добавлено: Исчезновение элементов
  2. Переместить: изменить положение элементов
  3. Удалить: Исчезающие элементы

Add OP уже особенный. Хотя мы можем положиться на CSS для создания эффекта постепенного появления:

transition: opacity 500ms ease-in-out;

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

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

Для операций перемещения нам просто нужно изменить абсолютные позиции. CSS помогает нам и здесь:

transition: transform 500ms ease-in-out;

Теперь мы можем присвоить позиции каждому элементу следующим образом:

transform: translate(200px, 300px);

Перед удалением OP нам нужно установить непрозрачность элемента на 0 и удалить элемент из DOM после завершения затухания.

Пока это звучит довольно просто, но становится намного сложнее, если мы хотим настроить элементы списка, когда переходы уже выполняются.

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

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

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

Использование движка виртуального DOM может значительно упростить поставленные задачи.

3. Как использовать анимированные списки в своих приложениях?

Реализация PoC разработана для фронтенд-фреймворка neo.mjs, поэтому использование анимированных списков в этой области тривиально:

В случае, если вы расширяете list.Base или перетаскиваете экземпляр списка в дерево компонентов, вы можете просто использовать конфигурацию animate.

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

Как видите, pluginAnimateConfig доступен для изменения конфигураций по умолчанию:

(вы можете поместить такой элемент в массив items контейнера)

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

4. Требование: реализация коллекции (хранилища данных)

В частности, если мы хотим реализовать представление данных с фильтрацией и сортировкой на стороне клиента, нам нужен класс коллекции, который может обрабатывать несколько фильтров и сортировщиков, а также возможность их динамической настройки.

Загляните в коллекцию. База, чтобы получить представление.

Внутри этого демонстрационного приложения мы используем следующий магазин:

Если присмотреться: мы хотим разрешить фильтрацию списка по «имени», которое представляет собой комбинацию совпадений внутри имени или фамилии. Вместо фильтрации с помощью заданных property, operator и value мы также можем передать пользовательскую функцию filterBy().

Это позволяет нам довольно легко подключать демонстрационные элементы управления для настройки фильтров и сортировщиков:

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

5. Погружение в list.plugin.Animate

Позвольте мне поделиться текущим снимком плагина, он содержит «всего» 350 строк кода:

Мы сосредоточимся на onStoreFilter() в строке 189.

Как упоминалось ранее, в случае, если мы не перехватываем запущенные анимации, логика довольно проста, так как событие filter дает нам data.items, а также data.oldItems, и это все, что нам нужно.

Однако при перехвате анимации порядок и количество элементов списка в DOM не обязательно должны соответствовать индексам data.items .

Как вы знаете, операции чтения и записи в реальном DOM обходятся дорого. Нам повезло, так как вместо этого мы можем просто получить доступ к this.owner.vdom.

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

При добавлении новых элементов нам просто необходима следующая проверка:

// flag items which are still inside the DOM (running remove OP)
if (intercept && map.includes(owner.getItemId(record[key]))) {
    item.reAdded = true;
}

→ только в том случае, если новые элементы не существуют внутри реального DOM на данный момент, мы добавим их с непрозрачностью 0 за один вызов requestAnimationFrame() до начала анимации.

Остальное прямо:

  1. добавлен элемент => непрозрачность 1
  2. перемещенный элемент =› новая позиция
  3. удаленный элемент => непрозрачность 0

Обратный вызов fn удалит все «удаленные» элементы из реального DOM.

Стоит также обратить внимание на динамическое изменение продолжительности перехода во время выполнения:

Мы могли бы применить новое значение transition к каждому элементу списка, что могло бы привести к большому количеству операций записи DOM.

Мы также могли бы использовать переменные CSS, но это повлияло бы на все экземпляры сразу.

Итак, вместо этого мы просто добавляем новое правило CSS в динамически добавляемую таблицу стилей.

6. Код и онлайн-демонстрации

Вы можете найти код нового плагина здесь:
src/list/plugin/Animate.mjs

Код демонстрационного приложения находится здесь:
examples/list/animate

dist/production
Минимизированная сборка на основе веб-пакета без исходных карт:
dist/production/examples/list/animate/index.html

режим разработки (только для Chromium или Safari)
Запуск реального JS-кода в том виде, в котором он находится, непосредственно в вашем браузере, без использования исходных карт: examples/list/animate/index.html

7. Идеи по улучшению

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











8. Изучаем neo.mjs

Фреймворк сильно вырос и развился, и это хорошо.

Хотя долгое время казалось достаточным просто поделиться знаниями в разделе блога:

https://neomjs.github.io/pages/

это определенно уже не так.

На самом деле мне очень нравятся новые документы React (престижность создателям!), и нам нужно что-то подобное для области видимости neo.mjs:



Например. при просмотре этой статьи:

Если бы мы напрямую сравнили эти предметы с neo:

  • состояние настройки НЕ запрашивает новый рендеринг (рендеринг происходит только один раз для основного представления вашего приложения)
  • neo хранит состояние внутри вашего компонента
  • нам не нужны снапшоты (persistent vdom tree)
  • переменные и обработчики событий выживают

Теперь вы, наверное, думаете:

«Вау, это прямо противоположное поведение! Итак, каковы плюсы и минусы?»

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

Однако, поскольку это фреймворк, его возможности намного больше, чем у экосистемы React. Мы могли бы легко получить от 50 до 100 руководств/разделов.

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



Заранее спасибо!

9. Accenture нанимает разработчиков neo.mjs в Германии

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

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

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

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

Не беспокойтесь, фреймворк останется с открытым исходным кодом.

Если вам нравится концепция компании: «Инновации в новом» и вы готовы принять вызов, чтобы изменить то, как работает фронтенд-разработка, вы можете присоединиться к этой команде. Поощрения привлекательные.

На данный момент роли ограничены Германией:



С уважением и удачного кодирования,
Тобиас

P.S.: На самом деле вы только что ощутили некоторые преимущества использования виртуального DOM на основе JSON. Реализация этой логики внутри управляемой шаблоном области действия кажется мне намного более сложной.

Изображение для предварительного просмотра: