Что такое «бесшовная прокрутка»
Так называемая «бесшовная прокрутка» означает, что процесс переключения между несколькими экранами является непрерывным и зацикленным, а не останавливает воспроизведение на последнем экране. Этот тип бизнес-сценария очень распространен в реальной разработке.Следующие — «Taobao» и «Jingdong».H5
Скриншот главной страницы версии, «баннер» и «заголовок» — типичные сцены с плавной прокруткой. Но испытав это, вы обнаружите, что между ними и эффектом в нативном приложении все еще есть определенный разрыв. Вы можете отсканировать код, чтобы открыть его и испытать на своем мобильном телефоне, а затем открыть их приложение и попробовать его, проведя пальцем по экрану.ты найдешьH5
В версии чего-то не хватает!
Таобао: Цзиндон:
Возможно, вы нашли его!H5
Похоже, что в этой версии не хватает суждения о намерении жеста пользователя: например, сцена на рисунке ниже, если вы находитесь на Taobao.H5
версия баннера, вы медленно качаете влево и вправо, это будет только простое сравнениеtouchstart
а такжеtouchend
Абсцисса при запуске события определяет, в каком направлении перемещаться на один экран. И если вы сделаете это в собственном приложении, в конце оно вернется к экрану, который занимает большую часть текущего экрана, то есть к первому экрану, и когда вы быстро проведете пальцем по тому же месту, он переключится на второй экран.
По сравнению с JingdongH5
Впечатления будут чуть хуже, он просто "не следит" за вами, когда вы скользите, а определяет направление только после того, как вы остановитесь (данная статья написана в марте 2019 года, с обновлением сайта опыт может быть другим. разные).
Как производитель бенчмаркинга, одни и те же продукты находятся на стороне приложения иH5
Они должны знать о различиях в сквозной производительности, но почему они несовместимы? Если вы хотите использовать интерфейсную технологию для достижения этой цели, это должно потребовать небольших дополнительных затрат на разработку или столкнуться с такими проблемами, как зависание. Давайте примерно проанализируем, как они в настоящее время добиваются «бесшовной прокрутки». Но прежде чем мы начнем, давайте посмотрим на:
Основы бесшовной прокрутки
Как показано на рисунке выше, мы располагаем три картинки в ряд, а именно № 1, № 2 и № 3. Из окна первой появляется картинка № 1. После непродолжительной остановки пролистайте до № 1. 2, а затем идите назад по очереди. , который является номером 3. Если мы хотим добиться «бесшовной прокрутки», а не заканчиваться на 3-м, то 1-й должен появиться следующим, потому что это может сформировать визуальную круговую прокрутку, поэтому нам нужно быть на 3-м. №1 позже. Когда эта «фальшивая» картинка №1 полностью прокручивается до заполнения экрана, мы быстро переводим все в начальное состояние. За счет этой «телепортации» из окна отображается полная картина №1, поэтому визуально «мутацию» за ней не почувствовать. Так как пользователь может листать влево и вправо посредством жестов, в свою очередь, в стартовой позиции должна быть добавлена картинка № 3, которая здесь повторяться не будет, таким образом, независимо от того, прокручивая влево или вправо, она будет формировать визуально бесшовный эффект.
Итак, каковы технические моменты, связанные с реализацией этого с точки зрения внешнего интерфейса?
- Реализация смещения: мы можем использовать
position
Позиционирование +left/top
значение, вы также можете использоватьtransform: translate(x, y)
метод, который лучше или хуже, ответ таков, что последний лучше. Заинтересованные могут прочитать эту справочную статьюWhy Moving Elements With Translate() Is Better Than Pos:abs Top/left. - «Одна команда и одно действие»: запуск и остановка, запуск и остановка. . . Понятно, что нужен таймер.
- "Движется как кролик": То есть анимация переключения перед двумя экранами. Например, стиль на первом экране на картинке выше:
transform: translate(0px, 0px)
, а на втором экране этоtransform: translate(-200px, 0px)
. Обычно для этого изменения требуется быстрая анимация перехода, а не мгновенная «мутация» с №1 на №2, в этом случае нужно использоватьtransition: translate 0.3s ease;
чтобы указать, что вы хотите, чтобы этот переход был плавным процессом. Здесь есть проблема: когда два экрана №1 нужно соединить и сбросить позиции, им нужно «мутировать», как на картинке выше.transform: translate(-600px, 0px)
прибытьtransform: translate(0px, 0px)
процесс. Это означает, что элемент спискаtransform
Атрибуты не являются статическими, поэтому вам нужно определить, является ли ток критическим состоянием до того, как таймер «One-Shot» начинает переключаться на следующий экран, чтобы установить разныеtransition
продолжительность. - Работа с жестами под мобильным терминалом: нам нужно использовать три события, связанные с прикосновением, а именно:
touchstart
(пальцем касаясь экрана),touchmove
(при скольжении он будет срабатывать постоянно) иtouchend
(пальцем от экрана). И что нам нужно сделать, это пройтиevent.touches
илиevent.changedTouches
Получите их информацию о координатах, когда эти события запускаются. например, когда пользовательtouchstart
При срабатывании мы записываем началоx
координаты оси,touchmove
При срабатывании мы сравниваем это времяx
Разница между координатами оси и координатами в начале, с помощьюtranslate
Перемещайтесь на такое же расстояние, чтобы добиться эффекта «следующей руки». и когдаtouchend
При срабатывании мы по-прежнему подтверждаем, хочет ли пользователь провести пальцем влево или вправо, сравнивая разницу с начальной координатой.
Все дороги ведут в Рим
Приведенное выше введение — лишь одна из наиболее распространенных идей для достижения «бесшовной прокрутки», Taobao кажется умнее: мы знаем, что палец пользователя на экране, самое дальнее расстояние непрерывного пролистывания не может превышать ширину экрана, например как и я. В это время на экране 2 я могу перейти на экран 1 или максимум на экран 3. В любом случае, я не могу перейти на экран 4 за раз. То есть, независимо от того, сколько у нас всего экранов, мы появляемся на экране одновременно.DOM
В лучшем случае он принадлежит двум соседним экранам. В этом случае мы можем поместить остальную часть экрана вочередь ожидания, пусть они остаются в фиксированном положении за пределами экрана. Таким образом, каждый раз, когда вы прокручиваете, область, перерисовываемая браузером, составляет всего два экрана — текущий экран и следующий экран. вместоn + 2
, что несомненно намекает на производительность. Я считаю, что благодаря следующей динамической картинке вы сможете понять, что я имею в виду:
Из приведенного выше рисунка видно, что одновременно перемещаются только два экрана, а позиции DOM трех экранов менялись во время каждой волны переключения. Следующие два скриншота также очень хорошо подтверждают мою догадку:
В конце концов, Али есть Али, а босс есть босс, я должен восхищаться этим! Напротив, JD.com немного грубее, используя основные принципы, которые я представил в начале. Хотя существуют различия в принципах бесшовной прокрутки, в основном используются четыре метода, которые я перечислил выше. Алгоритм реализации Таобао очень хорош, но есть фатальная проблема, его трудно удовлетворитьНажмите, чтобы переключитьсяЕсли на "индикатор в виде маленькой точки" внизу можно нажать, чтобы прыгнуть, то представьте, как он прыгает со второго экрана на четвертый экран? Но эта потребность характерна для «бесшовной прокрутки» на ПК, и как разработчик вы должны думать наперед. Оба также страдают от непонимания намерения жеста пользователя, о котором я упоминал в начале, поэтому пришло время представить новое решение:
seamless-scroll
Это подключаемый модуль бесшовной прокрутки, который я недавно использовал. Он подходит как для мобильных устройств, так и для сценариев разработки ПК.requestAnimationFrameа такжеtranslate
выполнить. Обеспечивает собственный интерфейс, подобный приложению, добавляя обработку сценариев жестов, таких как «быстрое пролистывание для переключения» и «медленное перетаскивание». Не зависит от каких-либо существующих фреймворков или библиотек компонентов, чистыйJS
, а это значит, что где бы вы ни находилисьVue
ещеReact
Можно использовать прямо в проекте. служба поддержкиnpmустановить иCDN-ссылкаимпорт,📦Gzip Size
< 3KB
,служба поддержкиIE10+
,IOS9+
,Andorid5+
и современные браузеры. Он также прост в использовании, он предоставляетSeamlessScroll
конструктор, вы можете использоватьnew
Ключевое слово создает экземпляр "бесшовной прокрутки". Передавая параметры, вы можете настроить скорость анимации, автоматическое воспроизведение и другие варианты поведения. Созданный экземпляр также обеспечиваетstart
,stop
,go
А другие методы позволяют легко управлять началом и остановкой воспроизведения или напрямую переходить к индексной позиции.
Адрес репозитория на гитхабе Отсканируйте код, чтобы испытать мобильный терминал Нажмите, чтобы просмотреть на ПК Пример кода, используемого в React
Протестировано на реальном iPhone и Xiaomi 5, работа по-прежнему очень плавная, на картинке ниже Google Chrome.Performance
Скриншот панели вверхуFPS
Колонна образует непрерывные и стабильные 5 зеленых маленьких блоков, отражающих 5 ходов во времяFPS
Изменение. Чем выше эти зеленые полосы, тем выше частота кадров и более плавное воспроизведение.Наоборот, если есть красные полосы, вероятно, будет отставание.
Ниже я представлю свои идеи реализации:
Сначала выберите основной принцип реализации: два принципа «бесшовной прокрутки» «стиль Taobao» и «стиль Jingdong», представленные выше, потому что для удовлетворения потребностей прямого перехода мы выбрали последний.
Переосмысление выбора технологии: выше представлены четыре технических момента, которые необходимо использовать для реализации «бесшовной прокрутки». ссылка "заяц". Выше мы говорили, что эту анимацию перехода можно использоватьtransition
для достижения, он работает очень плавно. Но мы знаемСуть анимации — это на самом деле группа непрерывных движущихся картинок., в этом случае мы можем пройтиНепрерывное перемещение на небольшое расстояние за короткий промежуток времениЧтобы добиться эффекта анимации? конечно может. Мы могли бы также абстрагировать процесс «бесшовной прокрутки», как иЦиклическая комбинация двух состояний - статического состояния и состояния анимации.
Стационарное состояниеДалее мы запустим следующую волну после задержки на время через таймер.состояние анимации, и для этогосостояние анимацииподтверждатьцелевое местоположение, пока всостояние анимацииДалее аккуратно «движемся» шаг за шагом, следя за тем, чтобы мы пришли в любое времяцелевое местоположение, если оно достигнуто, останавливаемся и возвращаемсяСтационарное состояние, и это подтверждает нашу следующую волну ходов. Идея уже ясна! Значит ли это, что мы можем пройти уже дваsetTimeout
сделать это?No
, потому что расстояние между теорией и реальностью, как любовь.
Рендеринг в браузере не происходит в одночасье — проблема в том, что "Непрерывное перемещение на небольшое расстояние за короткий промежуток времени», знайте, что в этом процессе вы должны подтвердить в режиме реального времени, прибыли ли выцелевое местоположение, то он будет включать чтение текущегоtranslate
сместить и установить новыйtranslate
работай. так частоDOM
Чтение и запись неизбежно приведут к отставанию! мы все знаемJS
Прямое управлениеDOM
это очень дорого! иначеVue
не нужноVNode
, правильно? Так как оптимизация процесса чтения и записи стала ключом к обеспечению плавности «анимации»!
читатьПроблема легко решается, мы можем поддерживать значение состояния смещения внутри, любоеDOM
серединаtranslate
Изменение значения должно быть сначала отражено в этом значении, аналогичноVue
а такжеReact
виртуальныйDOM
Роль дерева, но наша проще, просто отображение фактического смещения, чтобы текущее смещение не нужно было каждый раз считывать из фактического DOM.
НапишитеПроцесс неизбежен, без измененийDOM
Пользователь не увидит никаких изменений, так почему бы не начать с анимации!
Мы уже знаем, что черезtranslate
Сместить элемент по сравнению с Position +left/top
метод, преимущество которого заключается в том, что браузер непереставлять. И в этом сценарии используйтеtranslate3d
будет только хуже, потому чтоJS
Часто меняйте это свойство, браузер должен каждый раз сравниватьxyz
преобразование по трем осям, принуждениеGPU
Ускорение, кажется, стало метафизическим. Поэтому, когда "бесшовная прокрутка" идет вдольX
направлении, то лучший способ писать на самом делеtranslateX
, по аналогииY
Направление осиtranslateY
.
Время написания является нашей главной задачей. Если вы хотите, чтобы пользователь видел непрерывное изображение, это означает, что каждый1000 / 60 ms
то есть16.67 ms
Эта запись делается один раз или около того. мы знаемsetTimeout
На самом деле он не точен, он зависит от частоты обновления встроенных часов браузера, и он также сталкивается с проблемой этой асинхронной очереди, как и следующий фрагмент кода, который мы ожидаемsetTimeout
Печать через 3 секундыDone!
, но на самом деле это занимает 10 секунд, он будет «заблокирован» процессом синхронизации!
// 期望 3 秒后打印 Done!
setTimeout(function () {
console.log("Done!");
}, 1000 * 3);
// 这个同步进程需要 10s 才能从执行栈里推出,所以 10s 后才会打印 Done!
function waitSeconds(wait) {
var start = Date.now();
while (start + 1000 * wait > Date.now()) {}
}
waitSeconds(10);
благодаряrequestAnimationFrameСуществование этого API позволяет нам добиться плавной «бесшовной прокрутки» благодаря этой идее.
window.requestAnimationFrame()
Сообщите браузеру, что вы хотите выполнить анимацию, и попросите браузер вызвать указанную функцию обратного вызова, чтобы обновить анимацию перед следующей перерисовкой. Этот метод должен передать функцию обратного вызова в качестве параметра, функция обратного вызова будет выполнена до следующей перерисовки браузера.
правильноtransform
Модификация вызовет перерисовку, что означает, что мы можем сформировать непрерывный набор анимаций рекурсивным способом. Скопируйте следующий код в консоль браузера, чтобы испытать ощущение дрейфа страниц.
var target = 200
var offset = 0
function moveBody(){
document.body.style.transform = `translateX(${++offset}px)`
if(offset<target){
requestAnimationFrame(moveBody)
}
}
requestAnimationFrame(moveBody)
Таким образом,seamless-scrollбыл рожден. Есть больше деталей дизайна, например, как реализовать паузу и продолжить, как уведомить об изменении значения внешнего текущего индекса, как выяснить намерение жеста пользователя, если вы выбираете оптимальный путь перемещения, например, с 5-го экрана на 2-й экран, следуйте5,1,2
Последовательный ход лучше, чем5,4,3,2
порядок, потому что это то, что действительно создает визуально «бесшовный» эффект, а не движение назад. Если вам интересно, вы можете прочитать мойисходный код. Я также делал такие вещи, как добавлениеwill-changeПопытки оптимизировать свойства и т.д., но эффект не кажется очевидным. Критика и поправки от больших ребят приветствуются.Конечно, больше приветствуется пиар, особенно такой, который может значительно повысить производительность 😝. Далее краткое введение в использование этого плагина
Установить
npm i seamless-scroll
# 或者
yarn add seamless-scroll
быстрый старт
Рекомендуется обратиться к этомуDemoпроект, который включает
PC
End + пример кода для мобильных устройств
Чтобы плагин работал лучше, структура DOM страницы должна быть установлена в соответствии со следующими соглашениями:
<!-- 容器 -->
<div id="box">
<!-- 列表 -->
<ul>
<!-- 子元素们 -->
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<!-- 此处可以添加“小圆点指示器”或“前进后退箭头”等 DOM 元素-->
</div>
Инициализация экземпляра с «бесшовной прокруткой» так же проста 🍳, и потрясающая 💯 карусель баннеров готова:
// 引入插件
import SeamlessScroll from 'seamless-scroll';
// 创建实例
const scroller = new SeamlessScroll({
el: 'box',
direction: 'left',
width: 375,
height: 175,
autoPlay: false
});
// 用户点击“开始按钮”时,调用实例的 start 方法,开始播放
const startBtn = document.getElementById('start-btn');
startBtn.addEventListener('click', function() {
scroller.start();
});
параметр
имя параметра | иллюстрировать | необязательное значение | По умолчанию | Необходимый |
---|---|---|---|---|
el |
контейнерный элемент. уже можно получитьDOM предмет или элементid
|
DOMElement илиString
|
без | да |
direction |
направление прокрутки |
left , right , up , down
|
left |
нет |
width |
Ширина контейнера, в ед.px
|
Number |
без | да |
height |
Высота контейнера, в ед.px
|
Number |
без | да |
delay |
Время, проведенное на каждом экране, блокms
|
Number |
3000 |
нет |
duration |
Время, необходимое для прокрутки одного экрана, ед.ms
|
Number |
300 |
нет |
activeIndex |
Индекс отображаемого элемента по умолчанию в списке, начиная с0 Начинать |
Number |
0 |
нет |
autoPlay |
Начинать ли воспроизведение автоматически, если установленоfalse , экземплярstart метод запуска вручную |
Boolean |
true |
нет |
prevent |
Запрет прокрутки страницы, обычно используемый для вертикального воспроизведения, установлен наtrue Когда жест смахивания пользователя внутри компонента заставляет страницу прокручиваться вверх и вниз |
Boolean |
true |
нет |
onChange |
Функция обратного вызова при переключении между экранами, входным параметром является индекс текущего экрана, который можно использовать для таких сценариев, как настройка индикатора маленькой точки | Function |
без | нет |
метод экземпляра
start
При отсутствии автоматического воспроизведения вызовите этот метод, чтобы запустить воспроизведение вручную. можно вызвать только один раз, толькоautoPlay
дляfalse
И использовать его, не запуская.
stop
Остановить игру.
continue
Возобновить воспроизведение. Сотрудничатьstop
метод для использования.
go
Прокрутите непосредственно до позиции указателя или прокрутите один экран в определенном направлении. Вы можете использовать этот метод для быстрого перехода или переключения вперед и назад в бизнес-сценариях. Логика этого метода перехода заключается в выборе кратчайшего расстояния между целевым экраном и текущим экраном для смещения, например, от第5屏
прибыть第2屏
, последует5,1,2
движется по порядку вместо5,4,3,2
порядок, преимущество этого в том, что он действительно формирует визуальный "бесшовный" Эффект.
- Пример:
scroller.go(0)
илиscroller.go('left')
- Тип параметра:
Number
илиleft
,right
,up
,down
resize
Обновите ширину и высоту контейнера.
- Пример:
scroller.resize(375, 175) // width, height
- Тип параметра:
Number
,единица измеренияpx
Например, следующий код сбрасывает ширину и высоту контейнера после отслеживания изменения размера окна браузера.
(function(vm) {
var resizing,
resizeTimer,
requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
vm.resizeHandler = function() {
if (!resizing) {
// 第一次触发,停止 scroller 的滚动
resizing = true;
scroller.stop();
}
resizeTimer && clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
// 停下来后,重设 scroller 的宽高,并继续之前的播放
resizing = false;
scroller.resize(document.body.clientWidth, 300);
requestAnimationFrame(function() {
scroller.continue();
});
}, 100);
};
window.addEventListener('resize', vm.resizeHandler);
})(this);
Не забудьте очистить слушателя при выходе со страницы! Ниже приведенVue
изbeforeDestroy
Пример очистки прослушивателя смены окна в хуке
beforeDestroy(){
window.removeEventListener('resize', this.resizeHandler);
}
destroy
Уничтожить экземпляр, восстановив стиль элемента по умолчанию
Ниже приведенReact
изcomponentWillUnmount
Пример вызова метода в хуке:
componentWillUnmount(){
this.scroller.destroy()
}
Суммировать
Этот подключаемый модуль, обеспечивающий беглость речи, не только поддерживает интеллектуальное распознавание намерений пользователя, но также достаточен для удовлетворения бизнес-потребностей большинства проектов на стороне ПК и мобильных устройств. И он очень легкий и простой в использовании. Я надеюсь, что это может помочь друзьям, у которых есть потребности в этом отношении.Если у вас есть хорошие предложения, пожалуйста, оставьте сообщение для обмена.