Автор реально ленивый, эта статья уже несколько месяцев как зародилась идея. 😊
что такое iScroll
Во многих сценариях будет очень неудобно использовать прокрутку тела, в этот раз будет использоваться частичная прокрутка элемента, и будут происходить отвратительные вещи.
- Уродливые полосы прокрутки появятся в браузерах на стороне ПК в Интернете и в системе Windows. (На самом деле, есть и последние стили CSS, которые можно решить, и совместимость не очень хорошая)
- На мобильных телефонах браузер ios не может скользить инерционно и эластично (прокрутка браузера используется по умолчанию, а не-ios системная родная прокрутка), если добавить
-webkit-overflow-scrolling: touch;
Используя родную прокрутку системы, совместимость не очень хорошая, и баг не один и не два 😭. - Это не способствует реализации некоторых индивидуальных потребностей, таких как загрузка, обновление, подгонка и прокрутка и т. д.
Так совпало, что iScroll решает эти проблемы.
Автор iScroll — международный друг, его билет на самолет на githubздесь.
К сожалению, автор почти больше не поддерживает этот плагин iScroll, а соответствующих китайских документов в Интернете очень мало, но это не мешает нам брать этот плагин повсюду.
Давайте посмотрим, как работает iScroll
iScroll использует анимацию преобразования CSS3 для имитации эффекта инерционной и эластичной прокрутки, а эффект и производительность идеально близки к исходному эффекту прокрутки. Он также предоставляет множество функций, включая自定义滚动条,指定滚动到元素
и другие функции, это также может быть легко реализовано下拉刷新,上拉加载
.
Основное использование iScroll
Обязательно сначала установите его
npm install iscroll
yarn add iscroll
затем процитировать
import IScroll from 'iscroll/build/iscroll'; // 普通版
import IScroll from 'iscroll/build/iscroll-probe'; // 复杂版
import IScroll from 'iscroll/build/iscroll-infinite';
В iscroll есть несколько разных js-файлов: обычная версия, сложная версия и версия с бесконечной прокруткой. Здесь обычно используется сложная версия, которая поддерживает мониторинг позиции прокрутки в реальном времени, если мониторинг в реальном времени не требуется, можно использовать обычную версию.
Инициализировать с помощью
Вот пример фреймворка vue
<template>
<div class="wrap">
<div class="scroll-area">
<div v-for="n in 50" class="item">{{ n }}</div>
</div>
</div>
</template>
<script>
import IScroll from 'iscroll/build/iscroll-probe';
export default {
data() {
scroll: null,
},
mounted() {
// 提示,因为transform是对dom操作,所以需要在这个生命周期操作
this.scroll = new IScroll('.wrap', {
mouseWheel: true, // 允许鼠标滚轮
});
// 第一个参数是dom选择器,建议使用唯一性的id,这里以class为例
// 第二个参数为参数对象,是iscroll的一些配置
// 参数配置可以参考 http://wiki.jikexueyuan.com/project/iscroll-5/
}
}
</script>
<style>
.wrap{
height: 400px;
overflow: hidden;
/* 给滚动区域固定可滚动高度,并且超出隐藏 */
}
</style>
Приведенный выше код завершает простую инициализацию и использование iscroll, вы можете увидеть эффект
обновление iScroll
Обратите внимание, что, поскольку прокручиваемый контент может быть получен и загружен асинхронно в dom, если iscroll не обновляется, это может повлиять на функцию прокрутки, поэтому при загрузке асинхронного контента необходимо вызвать метод обновления для обновления iscroll. Метод обновления следующий
<template>
<div ref="scroll" class="wrap">
<div class="scroll-area">
<div v-for="n in 50" class="item">{{ n }}</div>
</div>
</div>
</template>
<script>
import IScroll from 'iscroll/build/iscroll-probe';
export default {
data() {
scroll: null,
},
mounted() {
const el = this.$refs.scroll;
this.scroll = new IScroll('.wrap', {
...
});
// ① 异步数据刷新
getData().then(_=>{
this.scroll.refresh();
})
// ② 首次滑动时刷新
el.addEventListener('touchstart', _=>this.scroll.refresh());
}
}
</script>
позиция прослушивания
this.scroll = new IScroll('.wrap', {
probeType: 3, // 滚动监听级别 有3档,3是像素级监听
});
// 用iscroll实例注册scroll事件
this.scroll.on('scroll', e => {
// 此处不用箭头函数可以用this.x和this.y访问实时位置,用了箭头函数需要从实例上访问
// this.scroll.x
// this.scroll.y
})
Нечего сказать, посмотрите эффект
Обратите внимание на положительное и отрицательное значения.Значение, полученное путем мониторинга, является значением преобразования, и подтвердите направление, соответствующее положительным и отрицательным значениям.
Прокрутите до указанной позиции элемента
Здесь вам нужно использовать функцию подгонки iscroll
this.scroll = new IScroll('.wrap', {
snap: '.item',
});
// 当设置snap属性为true时,iscroll会把容器可视区域分割为一个page
// 当设置snap属性为元素选择器时,iscroll会把对应的元素设置为一个page
// 这里我们设置为'.item'
Затем используйте IscrollgoToPage
метод, перейти к соответствующему элементу
this.scroll.goToPage(0, 30, 1000);
// 参数分别为x, y, 动画时间,
// 注意x,y是传入索引,第一个是0,类推
также можно использоватьprev
а такжеnext
способ пропустить предыдущий или следующий
this.scroll.prev();
this.scroll.next();
Настроить полосы прокрутки
Если вам нужна полоса прокрутки, это тоже очень просто
this.scroll = new IScroll('.wrap', {
scrollbars: true, // 开启滚动条
shrinkScrollbars: 'scale', // 超出滚动时,缩放滚动条
});
/* 因为iscroll的滚动条是定位实现,所以容器需要加一个相对定位 */
.wrap{
position: relative;
}
событие щелчка
iscroll по умолчанию отключает событие клика, при необходимости его можно включить
this.scroll = new IScroll('.wrap', {
click: true,
});
И iscroll имеет очень удобное встроенное событие касания, пока нажатие включено, оно может реагировать на касание элемента.
this.scroll = new IScroll('.wrap', {
tap: true,
});
<template>
<div ref="scroll" class="wrap">
<div class="scroll-area">
<div v-for="n in 50" class="item" @tap="onTap">{{ n }}</div>
</div>
</div>
</template>
iScroll липкий
В родной прокрутке, основанной на элементах dom, можно добавлять содержимое вposition: sticky
для достижения эффекта потолка.
Потолок: во время прокрутки родительского элемента, если дочерний элемент содержит
position: sticky
а такжеtop: 0
style, то когда содержимое прокручивается вверх, оно будет привязано к верху родительского элемента и больше не будет прокручиваться вверх. (То же самое для горизонтальной прокрутки)
никогда не играй в этоposition: sticky
Да, просто попробуй и узнай. Конечно, этот css не является непобедимым, причина в том, что не хватает совместимости.кликните сюда
Хорошо, давайте поговорим о том, как iscroll реализует sticky, потому что iscroll использует преобразование для реализации прокрутки, поэтому контейнер установленoverflow: hidden
, поэтому нет возможности использовать css sticky для достижения, а затем, поскольку преобразование родительского элемента прокручивается, то, когда оно достигает положения потолка, можно ли отменить преобразование дочернего элемента?
Присмотритесь к коду под ним, важно внимательно смотреть на комментарий ️
// 这段代码可以理解为是对iscroll类的扩展
// 这里的参数为iscroll类
export const extendSticky = (iScroll) => {
let m = Math;
// 这里是为了兼容性配置的浏览器css前缀,网络上有很多写法呢
let vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' :
(/firefox/i).test(navigator.userAgent) ? 'Moz' :
'opera' in window ? 'O' : '',
has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(),
trnOpen = 'translate' + (has3d ? '3d(' : '('),
trnClose = has3d ? ',0)' : ')';
/**
* 这里开始拓展iscroll类
* @param selector 需要sticky的对象集合,包含元素和sticky的位置
* @return { iScrollStickyHeaders }
*/
// 在iscroll原型上添加 enableStickyHeaders 方法
iScroll.prototype.enableStickyHeaders = function (selector) {
return new iScrollStickyHeaders(this, selector); // 拓展方法采用新的类并传参
};
// 参数,iscroll实例,需要sticky的元素集合
let iScrollStickyHeaders = function (iscroll, selector) {
if (!iscroll.options.useTransform) {
return;
}
this.iscroll = iscroll;
this.selector = selector;
this.initialize(); // 初始化
};
iScrollStickyHeaders.prototype = {
headers: [], // 存储需要sticky的对象集合
initialize() {
let that = this;
this._augment();
this.iscroll.on('refresh', function() {
that._refresh() // 每次iscroll刷新,sticky方法也刷新
});
this.iscroll.refresh()
},
_refresh() { // 初始化或者刷新
let elms = this.selector;
this.headers = [ // 深拷贝对象集合
...elms,
]
// 此处对象集合的格式为 { el: 元素, top: 需要sticky的位置 }
// 此处可以根据习惯和喜欢自行定义格式和逻辑代码
this._translate(0, 0); // 初始化
},
_augment() { // 初始化函数
let that = this;
this.iscroll.on('scroll', function() {
that._translate(this.x, this.y) // iscroll滚动时,触发主函数
});
this.iscroll.on('beforeScrollStart', function() {
that._translate(this.x, this.y) // iscroll即将滚动时,触发主函数
});
this.iscroll.on('scrollStart', function() {
that._translate(this.x, this.y) // iscroll开始滚动时,触发主函数
});
},
_translate(x, y) { // 主函数,到达sticky位置后,反向transform
let absY = m.abs(y); // 获取y轴滚动的绝对值
this.headers.forEach((stickyObj) => { // 遍历sticky对象
let translateY = 0; // sticky的反向transform默认为0
let yy = m.abs(absY - stickyObj.el.offsetTop); // 计算iscroll的y轴滚动值-当前元素距离父级的值
// stickyObj.el.offsetTop为固定值
// yy即为当前元素距离容器顶部的位置
// absY < stickyObj.el.offsetTop说明该元素还没到达顶部
// yy <= stickyObj.top 判断元素是否到达需要sticky的位置
// ① 当元素还没到达容器顶部时,默认为0,再判断是否到达指定sticky位置
// ② 如果没到达指定sticky,依然为0
// ③ 如果达到指定sticky位置,那么就计算超过sticky位置后,需要反向transform的距离
// ④ 这里默认指定位置是小于元素初始位置的,指定位置大于初始位置的,我想会很奇葩吧。
if (absY - stickyObj.el.offsetTop > 0 || yy <= stickyObj.top) {
// 这个公式需要反复理解一下
// 当容器往上滚动时,容器的transform是负值,所以我们反向是正值
// 容器向上滚动值absY不断变大,我们sticky就不断向下transform
// stickyObj.el.offsetTop - stickyObj.top 即为容器滚动多少范围才会让元素到达指定sticky位置
// 计算iscroll容器的滚动值 - (初始位置 - 指定位置)
// 当滚动值等于初始位置和指定位置之差时,刚好等于0
// 随着滚动值越来越大,超过0的部分,即为需要反向transform的值
translateY = absY - (stickyObj.el.offsetTop - stickyObj.top);
} else {
translateY = 0;
}
// 最后拼接浏览器前缀,完成css赋值
stickyObj.el.style[vendor + 'Transform'] = trnOpen + ('0, ' + translateY + 'px') + trnClose;
});
},
};
};
export default extendSticky;
Для облегчения понимания покажу навыки работы с Axure.
Хорошо, вышеiscroll-sticky.js
Инструмент готов, давайте начнем его использовать.
<template>
<div ref="scroll" class="wrap">
<div class="scroll-area">
<div v-for="n in 20" class="item">{{ n }}</div>
<div ref="sticky" class="sticky" :top="20">21</div>
<div v-for="n in 20" class="item">{{ n+20 }}</div>
</div>
</div>
</template>
<script>
import IScroll from 'iscroll/build/iscroll-probe';
import enableSticky from 'path/to/iscroll-sticky.js';
enableSticky(IScroll); // 这一步是将sticky方法挂载到iscroll原型上
export default {
data() {
scroll: null,
},
mounted() {
const el = this.$refs.scroll;
this.scroll = new IScroll('.wrap', {
...
});
const stickyEl = this.$refs.sticky;
// 允许元素对象集合sticky
this.scroll.enableStickyHeaders([
{
el: stickyEl,
top: stickyEl.getAttribute('top') // 此处我把top值配置在了原生prop
}
]);
}
}
</script>
Посмотрим на эффект.
над
iscroll-sticky.js
Это гибкий js, который можно настроить и изменить в соответствии с вашими потребностями.
Потяните вниз, чтобы обновить
На самом деле сам ISCrolle не имеет функции тяги к обновлению, но она может быть реализована сама по себе.
export default {
data() {
scroll: null,
status: 0, // 用一个变量记录iscroll滚动状态,默认为0
txt: '下拉刷新', // 记录刷新文本,默认
},
watch: {
status() {
// 每次iscroll的状态码变化时,就要刷新iscroll,以便iscroll重新计算dom元素
this.iscroll.refresh();
}
}
}
Затем добавьте текст обновления (или анимацию)
<template>
<div ref="scroll" class="wrap">
<div class="scroll-area">
<div :class="{hide: status===0}" class="refresh">{{ txt }}</div>
<div v-for="n in 50" class="item">{{ n }}</div>
</div>
</div>
</template>
.refresh{
width: 100%;
height: 50px;
line-height: 50px;
text-align: center;
&.hide{
/* 当status为0默认时,隐藏刷新文本,通过定位到容器外面 */
position: absolute;
left: 0;
top: -50px;
}
}
Затем прослушайте раскрывающийся список isscroll.
// ...
this.scroll.on('scroll', e => {
const y = this.iscroll.y; // 监听下拉的y值,下拉是正值
if (y >= 50) { // 当下拉距离>=刷新文本高度时,
this.status = 1; // 状态码变为1, 表示准备好刷新了
}
})
В настоящее время,status
становится 1, то мыhide
Обновляемый текст стал обычной загрузкой контентаiscroll
Теперь нужно четко понимать изменения dom, ключ в том, чтоОбновление iscroll после изменения состояния, наш палец не был отпущен, поэтому в настоящее время он готов к обновлению.В это время необходим новый монитор, чтобы следить за уходом пальца и остановкой прокрутки.
this.scroll.on('scroll', e => {
const y = this.iscroll.y; // 监听下拉的y值,下拉是正值
if (y >= 50) { // 当下拉距离>=刷新文本高度时,
this.txt = '释放刷新';
this.status = 1; // 状态码变为1, 表示准备好刷新了
} else if (y > 0) { // 如果返回了,又不想刷新了,恢复status为0
this.txt = '下拉刷新';
this.status = 0;
}
})
this.scroll.on('scrollEnd', e => {
if (status === 1) { // 滚动停止时,如果是准备刷新状态
this.txt = '刷新中。。。';
this.status = 2; // 改变状态码,开始刷新
this.scroll.disable(); // 刷新过程禁止滚动,这个禁用方法视需求而定。
this.updateData(); // 假设有一个更新数据的method
}
})
export default {
methods: {
updateData() {
getData().then(_=>{
// 数据更新完成
this.txt = '刷新完成';
// 延迟1秒后继续隐藏刷新文本
setTimeout(_=>{
this.txt = '下拉刷新';
this.status = 0; // 状态重置为0
this.scroll.enable();
}, 1000);
})
}
}
}
Взгляните на эффект демонстрации:
Вот демонстрация фильма «кошачий глаз», который я обычно делаю:
подтягивающая загрузка
Это также должно быть реализовано самостоятельно, но это очень просто, просто судите дно прокрутки.
this.scroll.on('scroll', e => {
// 此处scrollEl是容器高度,contentEl是内容高度,因为y是负值,所以用scrollEl - contentEl
if (this.scroll.y <= scrollEl.offsetHeight - contentEl.offsetHeight) {
// do something 上拉加载
}
});
Суммировать
iscroll — очень гибкая библиотека, которую можно свободно настроить в соответствии с желаемым эффектом.
Если вы знакомы с модульностью, вы можете попробоватьsticky
,下拉刷新
,上拉加载
упакован в компонент.
комментарий был упомянут
better-scroll
, нет проблем, используйте то, что вам нравится.
Добро пожаловать в лайки и избранное, продолжение, связанное с iscroll, будет обновляться со временем.