Реверсивный ползунок
Ниже приведено очень краткое объяснение того, как работает скрипт, включая предложение заставить его поменять направление и «отменить» карты. Метод обратного направления был вдохновлен превосходным ответом Рокки - он заслуживает полной оценки идеи.
Как только документ загружен, скрипт получает список всех доступных карточек. В вашем примере карты являются жестко закодированными элементами в DOM, а список карт listElNodesObj
представляет собой список этих элементов. Это важно помнить: карты - это не абстракция, это принципиально элементы на странице. Когда вы добавляете мультимедиа и данные к своим картам, вам придется делать это, прикрепляя их к элементам в DOM (например, с атрибутами данных).
Скрипт получает текущий индекс карты с именем currentPosition
; Для начала это верхняя карта. Затем отображается текущая карта и две карты за ней (currentPosition + 1
и currentPosition + 2
).
На входе карта соответствующим образом анимирована, чтобы вылететь слева, справа или сверху. Текущий индекс карты увеличивается на единицу, продвигаясь на единицу в стек. Отображается новая текущая карта и две карты за ней.
В данный момент все действия - проведите влево, вправо и вверх - все продвигаются в стек. Чтобы изменить направление, вам необходимо прослушать новое действие (или изменить текущее, например, проведите пальцем вверх), и при этом действующее значение индекса карты уменьшается на 1. Добавьте проверку меньше нуля. currentPosition = Math.max( 0, currentPosition - 1 );
Рокки ответил отличным решением, которое реализует это.
Теперь эта реализация начинается со всех карт, уже присутствующих в DOM. Похоже, вы хотите обновить свой стек карт из бэкэнд-API. Для этого вам нужен способ отсоединения карты от одного конца и добавления новой на другом конце при считывании. Как указывалось выше, ваш список карт очень тесно связан с DOM, поэтому вам придется немного его абстрагировать, чтобы добиться этого. Создайте список элементов, которые вы заполняете из своего API, и заполните его исходным документом (а не наоборот). Когда вы проводите пальцем вперед или назад, вытолкните элемент с отступающего конца и добавьте новый элемент, заполненный из вашего API, к продвигающемуся концу. Интересно, что размер вашего списка и ваша текущая позиция всегда будут оставаться неизменными.
Если ваша текущая позиция всегда на одну спину от самой верхней карты, а последняя карта в стеке на одну больше, чем последняя видимая карта, у вас всегда будет карта, готовая для анимации.
Производительность
Небольшое уточнение терминов: изменение что-то вроде opacity
вызывает перекрашивание, удаление элемента из DOM, мягкое или жесткое удаление, вызывает перекомпоновку. Перекрашивание обходится дорого, потому что браузер должен проверять видимость каждого элемента в DOM; Перерасходы еще дороже, потому что макет должен быть пересчитан. См. В чем разница между оплавлением и перекрашиванием?
Есть два способа ограничить количество карт в DOM. Вы можете установить display: none
, который оставляет его в памяти и DOM, но не позволяет браузеру учитывать его при переформатировании или перерисовке. Или вы можете использовать parent.appendChild(child)
для добавления карты и parent.removeChild(child)
для удаления карты, гарантируя, что в JavaScript не будет никакой ссылки на этот элемент после его удаления, а после запуска сборщика мусора удаленный элемент будет физически удален из памяти. Оба будут инициировать оплавление. Элементы с opacity: 0
полностью останутся в DOM для перекраски и перекраски.
Что касается того, что дает лучшую производительность: изменение непрозрачности или удаление из памяти, это действительно зависит от вашей реализации. Я могу дать вам несколько важных советов.
Ограничения памяти" Есть ли ограничение на количество карт в DOM? " Абсолютно, но это зависит от ваших данных. Если у вас очень небольшое количество карт, вы действительно можете загрузить их все в начале и спрятать считанные карты с помощью opacity: 0
или display: none
. Текучесть анимации может быть даже улучшена (см. Пункт о вычислениях блокировки анимации ниже). Разница в производительности исключительно из-за более высокого использования памяти почти наверняка будет незаметной, поскольку современные браузеры имеют кучу памяти и остановят ваш сценарий задолго до того, как ему потребуется файл подкачки или подкачка. Если у вас действительно будет такой огромный DOM в памяти, что производительность будет заметно ухудшаться, время загрузки вашего контента будет гораздо большей проблемой.
Тем не менее, гораздо важнее то, что вы справедливо спрашиваете, является ли удаление или добавление элементов целым пунктом виртуального списка. Зачем хранить элемент в памяти, если вы никогда не получите к нему доступ снова, или зачем загружать элемент, который находится так далеко внизу списка, он может никогда не быть достигнут. На самом деле вы заявляете, что вы будете получать доступ к контенту через API, что явно означает, что вы будете получать доступ к контенту карты по одному. Ожидание, пока у вас будет весь контент из вашего API, может занять очень заметное время; как вы уже знаете, это обеспечит лучший доступ только к тем карточкам, которые вам нужны для заполнения списка фиксированного размера. (Если вы намереваетесь изменить направление ползунка, вам следует сохранить в памяти как минимум одну карточку со сдвигом, чтобы при возврате назад вы не испортили анимацию, приостановив или отправив пустую карточку во время ожидания загрузки контента)
Расчеты блокировки анимации Помимо времени загрузки, реальное преимущество в производительности display: none
и opacity: 0
для карточек, которые считываются или находятся слишком далеко в списке, состоит в том, что их содержимое уже присутствует в DOM. (И, как указано выше, opacity: 0
имеет еще одно преимущество: он не запускает перекомпоновку). Для сравнения, физическое добавление и удаление элементов из DOM требует дополнительных вычислений, а именно вставки или удаления узла карты и всех его дочерних элементов в дереве DOM. Если это делается синхронно, то у вас будет вычисление блокировки анимации, в результате чего анимация смахивания не может иметь место до завершения обновления дерева DOM.
Однако давайте держать вещи в перспективе. Во-первых, добавление и удаление узлов из дерева DOM обычно происходит очень быстро. Было показано, что innerHTML немного быстрее при добавлении в DOM, но намного медленнее при удалении, поэтому выбирайте яд с осторожностью. Но если вы не добавите десятки или сотни килобайт данных на свои карты, операция , вероятно, займет менее миллисекунды . Во-вторых, вы заявляете, что будете получать контент для списка из API, что подразумевает асинхронное соединение. Если вы позаботитесь о создании асинхронных функций, ваша функция добавления контента в DOM не обязательно будет мешать анимации. (Не подразумевать, что JavaScript является многопоточным; он однопоточный, но хорошо сконструированный асинхронный код означает, что порядок выполнения функций не имеет значения). Если карты добавляются или удаляются, когда анимация не ставится в очередь, любой удар по производительности станет как на короче, так и менее ощутимым .
Наконец, каждая манипуляция DOM - это новое обновление рендера, поэтому вы хотите сделать как можно меньше манипуляций. Поэтому вы должны создавать элементы друг против друга в памяти и только в самом конце вставить самый высокий элемент в DOM. См. Самая быстрая вставка DOM . Если вы можете содержать все данные карты в этой карте, то для каждого пролистывания потребуется всего две манипуляции с DOM. Это зависит от того, какие носители содержатся в картах, но вполне возможно, что в худшем случае добавление и удаление карт в стек займет в совокупности только десятки миллисекунд.
Графический процессор Большинство механизмов рендеринга имеют доступ к графическому процессору, который может достичь гораздо большей эффективности, чем центральный процессор при операциях рисования и компоновки, в которых используется большое количество пикселей. Слои рендеринга по умолчанию не отображаются с помощью графического процессора Страница GPU Ускоренное наложение в Chrome состояний,
Хотя теоретически каждый RenderLayer мог бы рисовать себя на отдельной подложке [т.е. доступный для графического процессора слой композитинга], на практике это может быть довольно расточительным с точки зрения памяти (особенно VRAM).
Чтобы анимация, такая как изменение непрозрачности, отображалась с помощью графического процессора, вам необходимо получить доступ к анимации таким образом, чтобы она была неявно скомпонована . На этой странице представлен исчерпывающий список того, как это сделать, но, по сути, вы должны использовать CSS-анимацию для изменения непрозрачности, что побуждает браузер продвигать элемент в слой композиции. Текущий код, который вы представили, использует JavaScript для обновления непрозрачности каждого кадра анимации, поэтому изменение непрозрачности не является кандидатом на неявное наложение. (Вы, кажется, используете 3D-преобразования для анимации движения, которая запускает неявное наложение, так что, вероятно, оно уже оптимизировано для GPU). Изменение кода для использования CSS-анимации не является тривиальной задачей, но, скорее всего, это повысит производительность, особенно частоту кадров во время анимации. Конечно, для проверки этого в вашем конкретном сценарии требуется тестирование производительности, см. https://www.smashingmagazine.com/2016/12/gpu-animation-doing-it-right/ для обсуждения того, почему некоторые анимации на GPU могут работать медленнее.
Таким образом, увеличение производительности из-за динамического добавления и удаления элементов из виртуального списка, хотя оно может быть незначительно выше, чем при полной загрузке всего списка, скорее всего, составит всего несколько дополнительных миллисекунд за пролистывание. В асинхронной реализации частота кадров во время анимации не должна изменяться. Обычно это должно быть простой уступкой для вероятной значительной экономии времени начальной загрузки, но должно рассматриваться вместе с другими деталями вашей конкретной реализации.