实现简单的下拉刷新

在页面顶端,按住页面往下拉动一段距离再松开,页面就加载新的内容或刷新,这样的交互设计几乎已经成为了诸多移动端App的标配。最适合这个功能的莫过于微博和推特,以及一些需要经常检查是否有新内容的App。

今天心血来潮,尝试了几乎一下午,在几个难点卡了不短的时间,最后实现了最基础的功能。

1.gif

我们来看看这个过程。

先说说思路:

  1. 页面顶端隐藏了一个元素,只有把主页面下拉才会出现,它提醒用户松开即可刷新,并且在请求数据时显示一个 loading 提醒;

  2. 可以用负的 margin-top 把这个元素藏起来,页面下拉多长,就给它增加多少 margin-top,直到它完全显示出来;

  3. 我们需要监听3个事件:点击、拉动和松开。我们可以用拉动时的Y坐标减去点击时的Y坐标来获取拉动距离,把这个距离加到隐藏元素的 margin-top 上;监听松开是为了中途放弃时隐藏元素可以归位。

  4. 当隐藏元素完全暴露之后,就执行刷新操作。

接下来看看代码:

HTML 结构:


  
松开刷新
正文内容

CSS(隐去了无关功能实现的样式)

.container {
  width: 100vw;
  height: 100vh;
  border: 4px solid red;
}
.info {
  height: 40px;
  border: 4px solid blue;
  margin-top: -40px;
}

.main {
  border: 4px solid green;
}

JavaScript

const container = document.querySelector('.container');
const info = document.querySelector('.info');
const main = document.querySelector('.main');

let mouseDown = false;
let swipeStartingY = 0;
let swipeDownDistance = 0;

main.addEventListener('mousedown', function(e) {
  mouseDown = true;
  swipeStartingY = e.clientY;
});

main.addEventListener('mousemove', function(e) {
  if (!mouseDown) {
    return
  }
  swipeDownDistance = e.clientY - swipeStartingY;
  info.style.marginTop = swipeDownDistance - 40 + 'px';
});

document.addEventListener('mouseup', function() {
  mouseDown = false;
  if (swipeDownDistance >= 40) {
    info.textContent = "刷新中";
    console.log('执行刷新操作');
    return;
  }
  info.style.marginTop = "-40px";
});

现在下拉已经可以让隐藏元素 info 出现在视野中了,但是 info 会被拉出远超过自身高度的距离,更糟糕的是,info 还可以被往上推。所以我们要添加一些限制:


let mouseDown = false;
let swipeStartingY = 0;
let swipeDownDistance = 0;

main.addEventListener('mousedown', function(e) {
  mouseDown = true;
  swipeStartingY = e.clientY;
});

main.addEventListener('mousemove', function(e) {
  if (!mouseDown) {
    return
  }
  swipeDownDistance = e.clientY - swipeStartingY;
  if (swipeDownDistance > 40) {
    swipeDownDistance = 40;
  }
  if (swipeDownDistance <= 40 && swipeDownDistance >= 0) {
    info.style.marginTop = swipeDownDistance - 40 + 'px';
  }
});

document.addEventListener('mouseup', function() {
  mouseDown = false;
  if (swipeDownDistance >= 40) {
    info.textContent = "刷新中";
    console.log('执行刷新操作');
    return;
  }
  info.style.marginTop = "-40px";
});

现在我们给拉动距离 swipeDownDistance 设置了一个上限,上限就是 info 的高度,如果拉动距离超过上限或者为负,不要改变 info 的 margin-top,这样 info 的活动范围就被限制在自身高度内了。

一个体验问题

但是还有一个无关要紧但影响体验的问题:如果我们拖住页面不放,继续往下拉,拉出远超过 info 高度的距离,还是不放手,这时如果回头往上拉,info 不会在你开始往上拉的那一刻就缩回去。这是因为虽然我们限制了 swipeDownDistance 的上限,但 mousemove 事件中的 e.clientY 太大了,导致 swipeDownDistance 回不到 0 ~ 40 的范围内,因此不能满足改变 info 的 margin-top 的条件。

我的解决办法是,当下拉得太远时,重新定义一下记录拉动起点的 swipeStartingY,让它距现在鼠标所在的位置(e.clientY)最多只有一个 info 高度的距离,这样无论何时往上拉,swipeDownDistance 都会在0~40的范围内减小,从而满足改变 info 的 margin-top 的条件,info 自然就能被推回去了。

main.addEventListener('mousedown', function(e) {
  mouseDown = true;
  swipeStartingY = e.clientY;
});

main.addEventListener('mousemove', function(e) {
  if (!mouseDown) {
    return
  }

  swipeDownDistance = e.clientY - swipeStartingY;

  if (swipeDownDistance > 40) {
    swipeDownDistance = 40;
    swipeStartingY = e.clientY - 40;    //  添加这一句就能解决问题
  }
  if (swipeDownDistance <= 40 && swipeDownDistance >= 0) {
    info.style.marginTop = swipeDownDistance - 40 + 'px';
  }
});

document.addEventListener('mouseup', function() {
  mouseDown = false;
  if (swipeDownDistance >= 40) {
    info.textContent = "刷新中";
    console.log('执行刷新操作');
    return;
  }
  info.style.marginTop = "-40px";
});

到这里,下拉刷新最基本的功能和交互体验都完成了。不过这个实现还有一个坑,那就是放开点击时,info 是瞬间归位的,而最佳体验应该是让它慢慢回去。要实现这个目的就要添加一个动画,以后再填吧。

完整代码和预览:

https://jsbin.com/jaqigubado/1/edit?html,js,output

你可能感兴趣的:(实现简单的下拉刷新)