如何实现上拉加载,下拉刷新

87 阅读1分钟

上拉加载

image.png 上拉加载的本质是页面触底,或者快要触底时的动作

触底公式:scrollTop + clientHeight >= scrollHeight

简单实现:

<!DOCTYPE html>
<html>
  <head>
    <title>JavaScript Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="content">
      <!-- Initial content will be loaded here -->
    </div>

    <script src="./index.mjs" type="module"></script>
  </body>
</html>
#content {
  height: 80vh;
  overflow-y: scroll;
  border: 1px solid #ccc;
}
.item {
  padding: 20px;
  border-bottom: 1px solid #eee;
  background: blue;
}
import "./styles.css";

const content = document.getElementById("content");
let page = 1;

// Function to load more content
function loadMore() {
  for (let i = 0; i < 10; i++) {
    const item = document.createElement("div");
    item.className = "item";
    item.textContent = `Item ${page * 10 + i + 1}`;
    content.appendChild(item);
  }
  page++;
}

// Initial load
loadMore();

// Event listener for scroll
content.addEventListener("scroll", function () {
  let distance = 50; //距离视窗还用50的时候,开始触发;
  if (
    content.scrollTop + content.clientHeight >=
    content.scrollHeight - distance
  ) {
    console.log("加载更多");
    loadMore();
  }
});

下拉刷新

下拉刷新的本质是页面本身置于顶部时,用户下拉时需要触发的动作

关于下拉刷新的原生实现,主要分成三步:

  • 监听原生touchstart事件,记录其初始位置的值,e.touches[0].pageY
  • 监听原生touchmove事件,记录并计算当前滑动的位置值与初始位置值的差值,大于0表示向下拉动,并借助CSS3的translateY属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值;
  • 监听原生touchend事件,若此时元素滑动达到最大值,则触发callback,同时将translateY重设为0,元素回到初始位置

简单实现:

import "./styles.css";

const content = document.getElementById("content");
let page = 1;
let loading = false; // 防止重复加载

// Function to load more content
function loadMore() {
  if (loading) return; // 防止重复加载
  loading = true; // 开始加载

  // 模拟网络延迟加载数据
  setTimeout(() => {
    for (let i = 0; i < 10; i++) {
      const item = document.createElement("div");
      item.className = "item";
      item.textContent = `Item ${page * 10 + i + 1}`;
      content.appendChild(item);
    }
    page++;
    loading = false; // 加载完成
  }, 1000);
}

// Initial load
loadMore();

// Variables to handle pull down
let _refreshText = document.querySelector(".refreshText");
let startY = 0;
let _transitionHeight = 0;
let isPulling = false;

// Function to refresh content
function refresh() {
  console.log("refresh");
  content.innerHTML = ""; // 清空内容
  page = 1; // 重置页码
  loadMore(); // 重新加载内容
  _refreshText.innerText = "下拉刷新"; // 恢复提示文本
}

// Touch event listeners for pull down refresh
content.addEventListener("touchstart", function (event) {
  if (content.scrollTop === 0) {
    startY = event.touches[0].pageY; // 记录初始位置
    content.style.position = "relative";
    content.style.transition = "transform 0s";
    isPulling = true;
  }
});

content.addEventListener("touchmove", function (event) {
  if (isPulling) {
    _transitionHeight = event.touches[0].pageY - startY; // 计算下拉距离
    if (_transitionHeight > 0) {
      content.style.transform = "translateY(" + _transitionHeight + "px)";
      _refreshText.innerText = _transitionHeight > 55 ? "释放更新" : "下拉刷新";
    }
  }
});

content.addEventListener("touchend", function () {
  if (_transitionHeight > 55) {
    _refreshText.innerText = "正在更新..."; // 更新提示文本
    refresh(); // 下拉超过阈值,触发刷新
  } else {
    _refreshText.innerText = "下拉刷新"; // 恢复提示文本
  }
  // 复位视图
  content.style.transition = "transform 0.5s ease";
  content.style.transform = "translateY(0px)";
  isPulling = false; // 结束下拉状态
});

// Event listener for scroll (for loading more)
content.addEventListener("scroll", function () {
  if (content.scrollTop + content.clientHeight >= content.scrollHeight) {
    loadMore(); // 触发加载更多内容
  }
});