2

手摸手教你实现移动端滚动记忆(带分页)

 3 years ago
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.
neoserver,ios ssh client

手摸手教你实现移动端滚动记忆(带分页)

发布于 今天 04:23

在移动端网页中的列表如何实现滚动和搜索条件的记忆,类似微信文章一样,浏览到文章的某一个位置,在下次打开的时候依然能滚动到上次浏览的位置继续浏览。本篇文章介绍的是带分页的列表,如果你现在需求是不带分页的文章或列表当然也可以参考,实现方式基本是差不多的。

因为这里使用了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存储情况:

image.png

以下是列表请求的代码:

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK