Автор: Нань Фу
BetterScrollЭто подключаемый модуль с открытым исходным кодом, ориентированный на удовлетворение потребностей различных сценариев прокрутки на мобильном терминале (Адрес GitHub), подходит для сценариев приложений, таких как списки прокрутки, селекторы, карусельные изображения, индексные списки и инструкции по открытию экрана.
Чтобы соответствовать этим сценариям, он не только поддерживает гибкую настройку инерционной прокрутки, отскока границ, затухания полосы прокрутки и других эффектов, чтобы сделать прокрутку более плавной, но также предоставляет множество методов и событий API, чтобы мы могли быстрее реализовывать сценарии прокрутки. Требования, такие как обновление по запросу и загрузка по запросу.
Поскольку он реализован на основе собственного JavaScript и не зависит ни от какой платформы, на него можно ссылаться из собственного JavaScript или использовать в сочетании с текущей внешней средой MVVM, например, на официальном сайте.ПримерЭто комбинация с Vue.
Во-первых, давайте посмотрим, как это делает прокрутку более плавной.
Сделайте прокрутку более плавной
На мобильном телефоне, если вы использовалиoverflow: scroll
Создайте контейнер с прокруткой, и вы обнаружите, что его прокрутка тормозит и тормозит. Почему это происходит?
Поскольку мы давно привыкли к опыту прокрутки текущих основных операционных систем и окон браузера, например, прокрутке к краю, будет отскок, после того, как палец перестанет скользить, он будет продолжать прокручивать некоторое время по инерции, и страница будет быстро прокручиваться, когда палец быстро скользит. И этот вид нативного контейнера прокрутки не заставит людей чувствовать себя застрявшими.
Опыт прокрутки BetterScroll
Попробуйте прокрутку BetterScroll.Адрес опыта
Можно обнаружить, что после добавления инерционной прокрутки, отскока по краям и других эффектов он явно стал более плавным и комфортным. Так как же достигаются эти эффекты?
инерционная прокрутка
BetterScroll будет продолжать инерционную прокрутку некоторое время, когда пользователь завершит операцию смахивания. Сначала посмотрите на исходный кодBScroll.prototype._end
функция, которая является обработчиком событий touchend, mouseup, touchcancel и mousecancel, то есть логики в конце операции прокрутки пользователя.
BScroll.prototype._end = function (e) {
...
if (this.options.momentum && duration < this.options.momentumLimitTime && (absDistY > this.options.momentumLimitDistance || absDistX > this.options.momentumLimitDistance)) {
let momentumX = this.hasHorizontalScroll ? momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options)
: {destination: newX, duration: 0}
let momentumY = this.hasVerticalScroll ? momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options)
: {destination: newY, duration: 0}
newX = momentumX.destination
newY = momentumY.destination
time = Math.max(momentumX.duration, momentumY.duration)
this.isInTransition = 1
}
...
}
Функция приведенного выше кода заключается в использовании функции импульса для расчета расстояния и времени инерционной прокрутки, если инерционная прокрутка должна быть включена в конце операции скольжения пользователя. Эта функция рассчитывает расстояние прокрутки в соответствии со скоростью скольжения пользователя и параметром замедления - инерционным замедлением.Что касается времени прокрутки, это также настраиваемый параметр.
function momentum(current, start, time, lowerMargin, wrapperSize, options) {
...
let distance = current - start
let speed = Math.abs(distance) / time
...
let duration = swipeTime
let destination = current + speed / deceleration * (distance < 0 ? -1 : 1)
...
}
отскок края
Существует два шага обработки отскока, когда он превышает край: первый шаг — замедление при прокрутке за край, а второй шаг — отскок к краю. Среди них первый шаг находится в исходном кодеBScroll.prototype._move
функция, которая является обработчиком событий touchmove и mousemove, то есть логики во время операции скольжения пользователя.
// Slow down or stop if outside of the boundaries
if (newY > 0 || newY < this.maxScrollY) {
if (this.options.bounce) {
newY = this.y + deltaY / 3
} else {
newY = newY > 0 ? 0 : this.maxScrollY
}
}
Второй шаг – позвонитьBScroll.prototype.resetPosition
функция, которая возвращается к границам.
BScroll.prototype.resetPosition = function (time = 0, easeing = ease.bounce) {
...
let y = this.y
if (!this.hasVerticalScroll || y > 0) {
y = 0
} else if (y < this.maxScrollY) {
y = this.maxScrollY
}
...
this.scrollTo(x, y, time, easeing)
...
}
Плавная прокрутка — это только основы, истинная сила BetterScoll заключается в: предоставлении большого количества общих/настраиваемыхвариант вариант,Метод APIисобытие, так что различные требования к прокрутке могут быть реализованы более эффективно.
Как применять к различным сценариям спроса
Затем возьмем использование Vue в качестве примера, чтобы обсудить положение BetterScroll в различных сценариях.
Обычный прокручиваемый список
Например, есть следующий список:
<div ref="wrapper" class="list-wrapper">
<ul class="list-content">
<li @click="clickItem($event,item)" class="list-item" v-for="item in data">{{item}}</li>
</ul>
</div>
Мы хотим, чтобы он прокручивался вертикально, нам просто нужно сделать простую инициализацию контейнера.
import BScroll from 'better-scroll'
const options = {
scrollY: true // 因为scrollY默认为true,其实可以省略
}
this.scroll = new BScroll(this.$refs.wrapper, options)
Для использования BetterScroll во Vue есть момент, на который следует обратить внимание, потому что DOM-элемент списка не генерируется, когда рендеринг списка в шаблоне Vue не завершен, поэтому необходимо убедиться, что рендеринг списка завершен до создание экземпляра BScroll, поэтому в In Vue лучшее время для инициализации BScroll — это nextTick of mouted.
// 在 Vue 中,保证列表渲染完成时,初始化 BScroll
mounted() {
setTimeout(() => {
this.scroll = new BScroll(this.$refs.wrapper, options)
}, 20)
},
После инициализации контейнер-оболочку можно изящно прокручивать, и к нему можно получить доступ через экземпляр BScroll.this.scroll
Используйте методы и события API, которые он предоставляет.
Несколько часто используемых опций, методов и событий описаны ниже.
полоса прокрутки
scrollbar
Возможность настроить полосу прокрутки, по умолчанию false. Если установлено значение true или объект, включаются полосы прокрутки. Вы также можете использовать свойство fade, чтобы настроить, будет ли полоса прокрутки появляться и исчезать при операции прокрутки или будет отображаться все время.
// fade 默认为 true,滚动条淡入淡出
options.scrollbar = true
// 滚动条一直显示
options.scrollbar = {
fade: false
}
this.scroll = new BScroll(this.$refs.wrapper, options)
Конкретный эффект можно увидетьОбычный список с прокруткой — пример.
Потяните вниз, чтобы обновить
pullDownRefresh
Возможность настройки функции обновления по запросу. Если задано значение true или Object, обновление с протягиванием включено.Вы можете настроить верхнее расстояние протягивания вниз (порог), чтобы определить время обновления и дистанцию остановки отскока (стоп).
options.pullDownRefresh = {
threshold: 50, // 当下拉到超过顶部 50px 时,触发 pullingDown 事件
stop: 20 // 刷新数据的过程中,回弹停留在距离顶部还有 20px 的位置
}
this.scroll = new BScroll(this.$refs.wrapper, options)
Прослушайте событие pullingDown и обновите данные. И после обновления данных вызовите метод finishPullDown(), чтобы вернуться к верхней границе.
this.scroll.on('pullingDown', () => {
// 刷新数据的过程中,回弹停留在距离顶部还有20px的位置
RefreshData()
.then((newData) => {
this.data = newData
// 在刷新数据完成之后,调用 finishPullDown 方法,回弹到顶部
this.scroll.finishPullDown()
})
})
Конкретный эффект можно увидетьОбычный список с прокруткой — пример.
подтягивающая загрузка
pullUpLoad
Возможность настроить функцию загрузки с подтягиванием. Если установлено значение true или объект, можно включить загрузку с подтягиванием, а пороговое расстояние от дна можно настроить, чтобы определить время начала загрузки.
options.pullUpLoad = {
threshold: -20 // 在上拉到超过底部 20px 时,触发 pullingUp 事件
}
this.scroll = new BScroll(this.$refs.wrapper, options)
Прослушайте событие pullingUp, чтобы загрузить новые данные.
this.scroll.on('pullingUp', () => {
loadData()
.then((newData) => {
this.data.push(newData)
})
})
Конкретный эффект можно увидетьОбычный список с прокруткой — пример.
Селектор
wheel
Опции для включения и настройки селекторов. Вы можете настроить индекс текущего выбранного селектора (selectedIndex), кривизну списка (rotate) и время корректировки (adjustTime) для переключения выбора.
options.wheel = {
selectedIndex: 0,
rotate: 25,
adjustTime: 400
}
// 初始化选择器的每一列
this.wheels[i] = new BScroll(wheelWrapper.children[i], options)
Конкретный эффект можно увидетьселектор - пример.
Среди них селектор связи должен контролировать выбор каждого списка выбора, чтобы изменить другие списки выбора.
data() {
return {
tempIndex: [0, 0, 0]
}
},
...
// 监听每个选择列表的选择
this.wheels[i].on('scrollEnd', () => {
this.tempIndex.splice(i, 1, this.wheels[i].getSelectedIndex())
})
...
// 根据当前选择项,确定其他选择列表的内容
computed: {
linkageData() {
const provinces = provinceList
const cities = cityList[provinces[this.tempIndex[0]].value]
const areas = areaList[cities[this.tempIndex[1]].value]
return [provinces, cities, areas]
}
},
Конкретный эффект можно увидетьселектор - примерселектор связи в .
карусель
snap
Возможность включения и настройки карусели. Вы можете настроить зацикливание карусели (loop), ширину (stepX) и высоту (stepY) каждой страницы, порог переключения (threshold) и скорость переключения (speed).
options = {
scrollX: true,
snap: {
loop: true, // 开启循环播放
stepX: 200, // 每页宽度为 200px
stepY: 100, // 每页高度为 100px
threshold: 0.3, // 滚动距离超过宽度/高度的 30% 时切换图片
speed: 400 // 切换动画时长 400ms
}
}
this.slide = BScroll(this.$refs.slide, options)
Конкретный эффект можно увидетьКарусель — пример.
специальная сцена
В дополнение к основным сценариям прокрутки, таким как обычные списки прокрутки, селекторы и карусельные изображения, вы также можете использовать возможности, предоставляемые BetterScroll, для выполнения некоторых специальных сценариев.
индексный список
Для списка индексов, во-первых, вам необходимо отслеживать, к какой области индекса вы прокручиваете в режиме реального времени во время процесса прокрутки, чтобы обновить текущий индекс. В этом сценарии мы можем использоватьprobeType
Опция, когда для этой опции установлено значение 3, событие прокрутки будет отправляться в режиме реального времени на протяжении всего процесса прокрутки. Тем самым получая позицию в процессе прокрутки.
options.probeType = 3
this.scroll = new BScroll(this.$refs.wrapper, options)
this.scroll.on('scroll', (pos) => {
const y = pos.y
for (let i = 0; i < listHeight.length - 1; i++) {
let height1 = listHeight[i]
let height2 = listHeight[i + 1]
if (-y >= height1 && -y < height2) {
this.currentIndex = i
}
}
})
Когда индекс щелкнут, используйтеметод scrollToElement()Прокрутите до индексной области.
scrollTo(index) {
this.$refs.indexList.scrollToElement(this.$refs.listGroup[index], 0)
}
Конкретный эффект можно увидетьСписок индексов — пример.
загрузочный экран
Гид на начальном экране на самом деле представляет собой карусель с горизонтальной прокруткой, которая не зацикливается автоматически.
options = {
scrollX: true,
snap: {
loop: false
}
}
this.slide = BScroll(this.$refs.slide, options)
Конкретный эффект можно увидетьНачальная загрузка — пример. Поскольку эта сцена спроса обычно доступна только на мобильных терминалах, лучше всего увидеть эффект в режиме мобильного телефона.
Бесплатная прокрутка
freeScroll
Возможность включить свободную прокрутку, позволяющую прокручивать как по горизонтали, так и по вертикали, не ограничиваясь определенным направлением.
options.freeScroll = true
Также обратите внимание, что эта опцияeventPassthroughНедействительно, если установлена собственная прокрутка.
Конкретный эффект можно увидетьСвободная прокрутка — пример.
резюме
BetterScroll можно использовать почти во всех сценариях прокрутки, в этой статье представлены только варианты использования в некоторых типичных сценариях.
Как плагин, разработанный для удовлетворения потребностей мобильной прокрутки, множество опций, методов и событий, которые открывает BetterScroll, фактически дают нам возможность обрабатывать прокрутку быстрее, гибче и с точным временем.
Добро пожаловать в блог Didi FE:GitHub.com/DD Fe/DD Fe — нет…