

手摸手教你实现移动端滚动记忆(带分页)
source link: https://segmentfault.com/a/1190000040775466
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

手摸手教你实现移动端滚动记忆(带分页)
在移动端网页中的列表如何实现滚动和搜索条件的记忆,类似微信文章一样,浏览到文章的某一个位置,在下次打开的时候依然能滚动到上次浏览的位置继续浏览。本篇文章介绍的是带分页的列表,如果你现在需求是不带分页的文章或列表当然也可以参考,实现方式基本是差不多的。
因为这里使用了mui的下拉刷新的列表组件,要在tab切换时需要默认把滚动条重置到最顶部(从头开始浏览),refreshContainer为对应列表id
mui列表组件相关的代码可以参考我之前的文章:
【mui 多个tab页实现下拉刷新上拉加载】https://segmentfault.com/a/11...
mui("#refreshContainer").scroll().scrollTo(0, 0, 0);//tab切换时默认滚到顶部
在页面加载完成时定义一些工具函数:
localGet就是localStorage.getItem的一个简单封装
if (localGet("woAppInfo") == null){//首次加载读取上一次的查询条件 saveWoAppInfo({ tabId: "untreated", scrollTop: 0, }); } /** * 防抖函数 * @param {需要防抖的函数} func * @param {防抖时间段} delay */ function debounce(fn, wait) { var timer = null; return function () { if (timer !== null) { clearTimeout(timer); } timer = setTimeout(fn.bind(this), wait); }; } function saveWoAppInfo(woappInfo){ localStorage.setItem("woAppInfo", JSON.stringify(woappInfo)); }
当列表数据渲染完成时执行autoScrollTo滚动到上次位置
function autoScrollTo() { setTimeout(function () { var scrollTop1 = localGet("woAppInfo") && JSON.parse(localGet("woAppInfo")).scrollTop; // mui("#refreshContainer").scroll().scrollTo(0, -1000, 0); // $("#refreshContainer").scrollTop(scrollTop); mui("#refreshContainer").pullRefresh().scrollTo(0, scrollTop1, 0); }, 500); }
列表分页的代码逻辑因为不是文章主要内容就在这里就不给大家说明了,大致逻辑是用户滚动到底部(最后一条记录)->触发mui列表的上拉回调函数->通过当前分页数据对下一页数据继续进行获取->append到列表后面实现分页
queryObj
对象是在搜索和tab切换的的时候把搜索条件以key-value的形式存到里面,当在下次页面打开时就默认使用AppInfo中的搜索条件达到搜索条件记忆的效果。scroll.y
为当前滚动条距离到页面列表顶部的高度数值,因为在顶部往上拉时滚动条超出顶部就有可能是负数所以需要使用绝对值Math.abs
滚动事件初始化(autoScrollInit
)这里也要给组件的默认滚动到上次位置:
function autoScrollInit() { var scroll = mui("#refreshContainer").scroll({ scrollY: true, //是否竖向滚动 scrollX: false, //是否横向滚动 startY: localGet("woAppInfo") && JSON.parse(localGet("woAppInfo")).scrollTop, indicators: false, }); document.querySelector("#refreshContainer").addEventListener( "scroll", debounce(function () { var href = $(".A-tab span.active").attr("href"); var queryObj = JSON.parse(localGet("woAppInfo")).queryObj || {}; var pageSize = pages[href].pageSize; var listItemHeight = 110;//列表中单条记录的高度 queryObj.pageCount = Math.floor(Math.abs(scroll.y) / (pageSize * listItemHeight) + 1) * pageSize; //每页数量 = 当前下拉的距离长度/(一页的长度) * pageSize saveWoAppInfo({ tabId: href, queryObj: queryObj, scrollTop: Number(scroll.y.toFixed()), }); }, 1000) ); } var woAppInfo = localGet("woAppInfo") && JSON.parse(localGet("woAppInfo")); var tabId = woAppInfo.tabId; //自动选择上一次的顶部tab页 if (tabId){ $('span[href="' + tabId + '"]').addClass("active"); $(".list[id='" + tabId + "']").addClass("active"); }else{ $('span[href="untreated"]').addClass("active"); $(".list[id='untreated']").addClass("active"); }
分页滚动记忆原理:
listItemHeight为单条记录的高度,可以自己根据单条记录的高度来设定,有了这个高度然后通过当前列表中的滚动条y轴位置到顶部数值计算出滚动条到第一条记录的列表条数(pagecount) Math.floor(Math.abs(scroll.y) / (pageSize * listItemHeight) + 1) * pageSize;
这样做的原因是我们列表是分页加载的数据,所以为了下次能够滚到记忆的位置所以这里要计算一下记忆需要的记录条数和搜索条件,然后保存到 AppInfo中,在下次获取数据时通过pageCount每页数量去请求数据直接就能还原出上次的数据量这样才能和scroll.y的位置匹配得上。(因为如果上次滚动条位置是几百条的y轴位置,但这次记忆滚动的时候却才加载了几条数据,所以就导致因为数据不足自然就没办法滚动到几百条的位置)
在列表数据请求时(搜索或者初始加载),如果是普通的初始进入的加载才进行记忆,在列表渲染完成后首先初始化监听列表的滚动事件,然后再还原上次滚动条的位置(autoScrollTo
直接自动滚动到对应位置),并在滚动时取出当前的搜索条件和tab的id用于保存,注意这里为了性能问题需要设置防抖,抖到1s后再保存数据
执行saveWoAppInfo后的AppInfo存储情况:
以下是列表请求的代码:
var woAppInfo = localGet("woAppInfo") && JSON.parse(localGet("woAppInfo")); if (woAppInfo.queryObj && woAppInfo.queryObj.pageCount) { data.data.pageCount = woAppInfo.queryObj.pageCount; } ajax(data, { success: function (json) { var woAppInfo = localGet("woAppInfo") && JSON.parse(localGet("woAppInfo")); if (data.data.isSearch) { //当携带查询条件时默认滚到顶部 mui("#refreshContainer").scroll().scrollTo(0, 0, 0); mui("#refreshContainer").pullRefresh().scrollTo(0, 0, 0); } if ( data.data.isSearch == undefined && woAppInfo.tabId === data.data.href ) {//当首次进入时对当前tab进行滚动记忆 autoScrollInit(); autoScrollTo(); } }, fail: function (json) { ... }, error: function (e) { ... }, });
当列表触发下拉刷新回调时重新清除搜索条件(因为相当于重新加载列表 不算上条件了),并保存当前tabid:
//清空查询参数 function clearSearchParams() { cacheObj = { //存储搜索时的搜索条件 xxxx: "", aaa: "", id: href === "all" ? user.id : "", }; saveWoAppInfo({ tabId: href, scrollTop: 0 }); } function pulldownRefresh() { var href = $(".A-tab span.active").attr("href"); clearSearchParams(href); fetchData(function () {//加载列表 mui("#refreshContainer").pullRefresh().endPulldownToRefresh(); }); }
本篇主要说明了移动端h5中的分页列表如何进行滚动的记忆
简单来说就是通过浏览器的Scroll事件对列表滚动条的y轴位置保存到localStorage中,在下次加载再通过自动滚动的方法滚动到上次位置,但是注意要通过计算还原出上次所记忆的数据量,因为需要足够的y轴空间去滚动。
如果觉得好的话希望大家给个赞~ 非常感谢
【手摸手教你实现页面tab和导航菜单记忆功能】https://segmentfault.com/a/11...
Recommend
-
93
-
44
前言: VuePress是尤大为了支持 Vue 及其子项目的文档需求而写的一个项目,VuePress界面十分简洁,并且非常容易上手,一个小时就可以将项目架构搭好。现在已经有很多这种类型的文档,如果你有写技术文档的项目的话,VuePress绝对可以成为你的备选项
-
71
前言 在本篇文章之前,WebSocket很多人听说过,没见过,没用过,以为是个很高大上的技术,实际上这个技术并不神秘,可以说是个很容易就能掌握的技术,希望在看完本文之后,马上把文中的栗子拿出来自己试一试,实践出真知。 游泳、健身了解一下:博客、前端积累文档...
-
36
-
68
在前端项目的开发中,往往会根据业务需求,沉淀出一些项目内的UI组件/功能模块(以下通称组件) 等;这些组件初期只在同一个项目中被维护,并被该项目中的不同页面或模块复用,此时的组件逐步被完善,是一个只聚焦于功能和健壮性的成长期。 随着业务的发展,原来的...
-
25
项目选型与环境搭建 项目选型 三大框架里选哪个? react 个人爱好。 react-router 定义路由。 react context 状态管理。 re...
-
21
这篇文章目的是介绍如何创建一个ESLint插件和创建一个 ESLint rule ,用以帮助我们更深入的理解ESL...
-
22
这篇文章目的是介绍如何创建一个ESLint插件和创建一个ESLint rule,用以帮助我们更深入的理解ESLint的运行原理,并且在有必要时可以根据需求创建出一个完美满足自己需求的Lint规则。 插件目标 禁止项目中setTimeout的第二个参数是数字。
-
36
前言: 首先,我的心路历程如下: 然后就成了。目前使用正常,还差个外置驱动,就可以实现蓝牙+隔空投送功能。 而这样一部3A大作通吃,27寸 4k屏的生产力黑苹果主机,我仅花了三千块组装。 接下来将从选择配件,到驱动双系统通通给你们讲明白。 1. 前置准备
-
7
分页与无限滚动在评论区的设计差异呆呆只是个会写点字的普通设计师本篇文章的问题挺有意...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK