狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

【HTML5】H5 下拉刷新如何實現

admin
2024年3月30日 0:12 本文熱度 670

本文轉載于稀土掘金技術社區——小霖家的混江龍

最近我需要做一個下拉刷新的功能,實現功能后我發現,它需要處理的情況還蠻多,于是我整理了這篇文章。

下圖是我實現的效果,分為三步:開始下拉時,屏幕頂部會出現加載動畫;加載過程中,屏幕頂部高度保持不變;加載完成后,加載動畫隱藏。

pull-down.gif

首先我會講解下拉的原理、根據原理寫出初始代碼;然后我會說明代碼存在的缺陷、解決缺陷并做些額外優化;最后我會給出完整代碼,并做一個總結。

拳打 H5,腳踢小程序。我是「小霖家的混江龍」,關注我,帶你了解更多實用的前端武學。

下拉的原理

prinple.png

如圖所示,藍色框代表視口,綠色框代表容器,橙色框代表加載動畫。最開始時,加載動畫處于視口外;開始下拉之后,容器向下移動,加載動畫從上方進入視口;結束下拉后,容器又開始向上移動,加載動畫也從上方退出視口。

下拉基礎代碼

知道原理,我們現在開始寫實現代碼,首先是布局的代碼:

布局代碼

我們把 box 元素當作容器,把 loader-box,loader-box + loading 元素當作動畫,至于 h1 元素不需要關注,我們只把它當作操作提示。

<div id="box">
  <div class="loader-box">
    <div id="loading"></div>
  </div>
  <h1>下拉刷新 ↓</h1>
</div>

loader-box 的高度是 80px,按上一節原理中的分析,初始時我們需要讓 loader-box 位于視口上方,因此 CSS 代碼中我們需要把它的位置向上移動 80px。

.loader-box {
  position: relative;
  top: -80px;
  height80px;
}

loader-box 中的 loader 是純 CSS 的加載動畫。我們利用 border 畫出的一個圓形邊框,左、上、右邊框是淺灰色,下邊框是深灰色:

loader.png

#loader {
  width25px;
  height25px;
  border3px solid #ddd;
  border-radius50%;
  border-bottom3px solid #717171;
  transformrotate(0deg);
}

開始刷新時,我們給 loader 元素增加一個動畫,讓它從 0 度到 360 度無限旋轉,就實現了加載動畫:

loading.gif

#loader.loading {
  animation: loading 1s linear infinite;
}

@keyframes loading {
  from { transformrotate(0deg); }
  to { transformrotate(360deg); }
}

邏輯代碼

看完布局代碼,我們再看邏輯代碼。邏輯代碼中,我們要監聽用戶的手指滑動、實現下拉手勢。我們需要用到三個事件:

  • touchstart[1] 代表觸摸開始;
  • touchmove[2] 代表觸摸移動;
  • touchend[3] 代表觸摸結束。

從 touchstart 和 touchmove 事件中我們可以獲取手指的坐標,比如 event.touches[0].clientX 是手指相對視口左邊緣的 X 坐標,event.touches[0].clientY 是手指相對視口上邊緣的 Y 坐標;從 touchend 事件中我們則無法獲得 clientX 和 clientY

我們可以先記錄用戶手指 touchstart 的 clientY 作為開始坐標,記錄用戶最后一次觸發 touchmove 的 clientY 作為結束坐標,二者相減就得到手指移動的距離 distanceY。

設置手指移動多少距離,容器就移動多少距離,就得到了我們的邏輯代碼:

const box = document.getElementById('box')
const loader = document.getElementById('loader')
let startY = 0, endY = 0, distanceY = 0

function start(e{
  startY = e.touches[0].clientY
}

function move(e{
  endY =  e.touches[0].clientY
  distanceY = endY - startY
  box.style = `
    transform: translateY(${distanceY}px);
    transition: all 0.3s linear;
  `

}

function end({
  setTimeout(() => {
    box.style = `
      transform: translateY(0);
      transition: all 0.3s linear;
    `

    loader.className = 'loading'
  }, 1000)
}

box.addEventListener('touchstart', start)
box.addEventListener('touchmove', move)
box.addEventListener('touchend', end)

邏輯代碼實現一個簡陋的下拉效果,當然現在還有很多缺陷。

pull-down-basic.gif

簡陋下拉效果的 6 個缺陷

之前我們實現了簡陋的下拉效果,它還需要解決 6 個缺陷,才能算一個完善的功能。

沒有最小、最大距離限制

第一個缺陷是,下拉沒有做最小、最大距離的限制。

通常來說,我們下拉屏幕時,距離太小應該不能觸發刷新,距離太大也不行,下滑到一定距離后,就應該無法繼續下滑。

因此我們可以給下拉設置最小距離限制 DISTANCE_Y_MIN_LIMIT、最大距離限制 DISTANCE_Y_MAX_LIMIT。如果 touchend 中發現下拉距離小于最小距離,直接不觸發加載;如果 touchmove 中下拉距離超過最大距離,頁面只向下移動最大距離。

解決缺陷關鍵代碼如下:

const DISTANCE_Y_MAX_LIMIT = 150
  DISTANCE_Y_MIN_LIMIT = 80

function move(e{
  endY =  e.touches[0].clientY
  distanceY = endY - startY
  if (distanceY > DISTANCE_Y_LIMIT) {
    distanceY = DISTANCE_Y_LIMIT
  }
  box.style = `
    transform: translateY(${distanceY}px);
    transition: all 0.3s linear;
  `

}

function end({
  if (distanceY < DISTANCE_Y_MIN_LIMIT) {
    box.style = `
      transform: translateY(0px);
      transition: all 0.3s linear;
    `

    return
  }
  ...
}

加載動畫沒有停留在視口頂部

第二個缺陷是,下拉沒有讓加載動畫停留在視口頂部。

我們可以把 end 函數加以改造,在數據還沒有加載完成時(用 setTimeout 模擬的),讓加載動畫 style 的 translateY 一直是 80px,translateY(80px) 可以和 初始 CSS 的 top: -80px; 相互抵消,讓動畫在未刷新完成前停留在視口頂部。

function end({
  ...
  box.style = `
    transform: translateY(80px);
    transition: all 0.3s linear;
  `

  loader.className = 'loading'
  setTimeout(() => {
    box.style = `
      transform: translateY(0px);
      transition: all 0.3s linear;
    `

    loader.className = ''
  }, 1000)
}

重復觸發

第三個缺陷是,下拉可以重復觸發。

正常來說,如果我們已經下拉過,數據正在加載中時,我們不能繼續下拉。

我們可以增加一個加載鎖 loadLock。當加載鎖開啟時,start,move 和 end 事件都不會觸發。

let loadLock = false

function start(e{
  if (loadLock) { return }
  ...
}

function move(e{
  if (loadLock) { return }
  ...
}

function end(e{
  if (loadLock) { return }
  ...
  setTimeout(() => {
    ...
    loadLock = true
    ...
  }, 1000)
}

沒有限制方向

第四個缺陷是,沒有限制方向。

目前我們的代碼,用戶上拉也能觸發。我們可以增加判斷,當 endY - startY 小于 0 時,阻止 touchmove 和 touchend 的邏輯。

function move(e{
  ...
  if (endY - startY < 0) { return }
  ...
}

function end({
  if (endY - startY < 0) { return }
  ...
}

你可能會疑惑,為什么我寧愿寫多個判斷攔截,也不取消監聽事件。這是因為一旦取消監聽事件,我們需要考慮在一個合適的時間重新監聽,這會把問題變得更復雜。

沒有阻止原生滾動

第五個缺陷時,我們在加載數據時沒有阻止原生滾動。

雖然我們已經阻止了重復下拉,touchmove 和 touchend 事件被攔截了,但是 H5 原生滾動還能用。

我們可以在刷新時給 body 設置一個 overflow: hidden; 屬性,刷新結束后清除 overflow: hidden,這樣就可以阻止原生滾動。

body.overflowHidden {
  overflow: hidden;
}
const body = document.body
function end({
  ...
  box.style = `
    transform: translateY(80px);
    transition: all 0.3s linear;
  `

  loader.className = 'loading'
  body.className = 'overflowHidden'
  setTimeout(() => {
    ...
    box.style = `
      transform: translateY(0px);
      transition: all 0.3s linear;
    `

    loader.className = ''
    body.className = ''
  }, 1000)
}

沒有阻止 iOS 橡皮筋效果

第 6 個缺陷是,沒有阻止 iOS 的橡皮筋效果。

iOS 瀏覽器默認滑動時有一個橡皮筋效果,我們需要阻止它,避免影響我們的下拉手勢。阻止方式就是給監聽器設置 passive: false

function addTouchEvent({
  box.addEventListener('touchstart', start, { passivefalse })
  box.addEventListener('touchmove', move, { passivefalse })
  box.addEventListener('touchend', end, { passivefalse })
}

addTouchEvent()

解決完 6 個缺陷后,我們已經得到無缺陷的下拉刷新功能,但離絲滑的下拉刷新還有一段距離。我們還可以做一些優化,讓下拉刷新更完善。

優化

我們可以做兩個優化,第一個優化是添加阻尼效果:

增加阻尼效果

所謂阻尼效果,就是下拉過程我們可以感受到一股阻力的存在,雖然我們下拉力度是一樣的,但距離的增加速度變慢了。用物理術語表示的話,就是加速度變小了。

體現到代碼上,我們可以設置一個百分比,百分比會隨著下拉距離增加而減少,把百分比乘以距離當作最后的距離。

代碼中百分比 percent 設為 (100 - distanceY * 0.5) / 100,當 distanceY 越來越大時,百分比 percent 越來越小,最后再把 distanceY * percent 賦值給 distanceY

function move(e{
  ...
  distanceY = endY - startY
  let percent = (100 - distanceY * 0.5) / 100
  percent = Math.max(0.5, percent)
  distanceY = distanceY * percent
  if (distanceY > DISTANCE_Y_MAX_LIMIT) {
    distanceY = DISTANCE_Y_MAX_LIMIT
  }
  ...
}

利用角度判斷用戶下拉意圖

第二個優化是利用角度判斷用戶下拉意圖。

下圖展示了兩種用戶下拉的情況,β 角度比 α 角度小,角度越小用戶下拉意圖越明顯、誤觸的可能性更小。

intension.png

我們可以利用反三角函數求出角度來判斷下拉意圖。

JavaScript 中,反正切函數是 Math.atan(),需要注意的是,反正切函數算出的是弧度,我們還需要將它乘以 180 / π 才能獲取角度。

下面的代碼中,我們做了一個限制,只有角度小于 40 時,我們才認為用戶的真實意圖是想要下拉刷新。

const DEG_LIMIT = 40
function move(e{
  ...
  distanceY = endY - startY
  distanceX = endX - startX
  const deg = Math.atan(Math.abs(distanceX) / distanceY)
    * (180 / Math.PI)
  if (deg > DEG_LIMIT) {
    [startY, startX] = [endY, endX]
    return
  }
  ...
}

代碼示例

你可以在 codepen[4] 中查看效果,web 端需要按 F12 用手機瀏覽器打開。

codepen.gif

總結

本文講解了下拉的原理、并根據原理寫出初始代碼。在初始代碼的基礎上,我解決了 6 個缺陷、做了 2 個優化,實現了一個完善的下拉刷新效果


該文章在 2024/3/30 0:12:59 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved