宅男在线永久免费观看网直播,亚洲欧洲日产国码无码久久99,野花社区在线观看视频,亚洲人交乣女bbw,一本一本久久a久久精品综合不卡

全部
常見問題
產(chǎn)品動態(tài)
精選推薦

H5下拉刷新功能實現(xiàn)攻略!

管理 管理 編輯 刪除

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

d481e20240305112550588.png

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

95f5e202403051126382905.png


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

下拉基礎(chǔ)代碼

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

布局代碼

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

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

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

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

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

#loader {
  width: 25px;
  height: 25px;
  border: 3px solid #ddd;
  border-radius: 50%;
  border-bottom: 3px solid #717171;
  transform: rotate(0deg);
}

開始刷新時,我們給 loader 元素增加一個動畫,讓它從 0 度到 360 度無限旋轉(zhuǎn),就實現(xiàn)了加載動畫。

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

@keyframes loading {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

邏輯代碼

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

touchstarttouchmove 事件中我們可以獲取手指的坐標(biāo),比如 event.touches[0].clientX 是手指相對視口左邊緣的 X 坐標(biāo),event.touches[0].clientY 是手指相對視口上邊緣的 Y 坐標(biāo);從 touchend 事件中我們則無法獲得 clientXclientY。

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

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

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)

邏輯代碼實現(xiàn)一個簡陋的下拉效果,當(dāng)然現(xiàn)在還有很多缺陷。

簡陋下拉效果的 6 個缺陷

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

沒有最小、最大距離限制

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

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

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

解決缺陷關(guān)鍵代碼如下:

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 函數(shù)加以改造,在數(shù)據(jù)還沒有加載完成時(用 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)
}

重復(fù)觸發(fā)

第三個缺陷是,下拉可以重復(fù)觸發(fā)。

正常來說,如果我們已經(jīng)下拉過,數(shù)據(jù)正在加載中時,我們不能繼續(xù)下拉。

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

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)
}

沒有限制方向

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

目前我們的代碼,用戶上拉也能觸發(fā)。我們可以增加判斷,當(dāng) endY - startY 小于 0 時,阻止 touchmovetouchend 的邏輯。

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

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

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

沒有阻止原生滾動

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

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

我們可以在刷新時給 body 設(shè)置一個 overflow: hidden; 屬性,刷新結(jié)束后清除 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 瀏覽器默認滑動時有一個橡皮筋效果,我們需要阻止它,避免影響我們的下拉手勢。阻止方式就是給監(jiān)聽器設(shè)置 passive: false。

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

addTouchEvent()

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

優(yōu)化

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

增加阻尼效果

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

體現(xiàn)到代碼上,我們可以設(shè)置一個百分比,百分比會隨著下拉距離增加而減少,把百分比乘以距離當(dāng)作最后的距離。

代碼中百分比 percent 設(shè)為 (100 - distanceY * 0.5) / 100,當(dāng) 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
  }
  ...
}

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

第二個優(yōu)化是利用角度判斷用戶下拉意圖。

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

cb2a8202403051130058243.png


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

JavaScript 中,反正切函數(shù)是 Math.atan(),需要注意的是,反正切函數(shù)算出的是弧度,我們還需要將它乘以 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 中查看效果,web 端需要按 F12 用手機瀏覽器打開。

總結(jié)

本文講解了下拉的原理、并根據(jù)原理寫出初始代碼。在初始代碼的基礎(chǔ)上,我解決了 6 個缺陷、做了 2 個優(yōu)化,實現(xiàn)了一個完善的下拉刷新效果。


請登錄后查看

CRMEB-慕白寒窗雪 最后編輯于2024-03-05 11:33:46

快捷回復(fù)
回復(fù)
回復(fù)
回復(fù)({{post_count}}) {{!is_user ? '我的回復(fù)' :'全部回復(fù)'}}
排序 默認正序 回復(fù)倒序 點贊倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理員 企業(yè)

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
{{item.is_suggest == 1? '取消推薦': '推薦'}}
沙發(fā) 板凳 地板 {{item.floor}}#
{{item.user_info.title || '暫無簡介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打賞
已打賞¥{{item.reward_price}}
{{item.like_count}}
{{item.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回復(fù) {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打賞
已打賞¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)
查看更多
打賞
已打賞¥{{reward_price}}
1986
{{like_count}}
{{collect_count}}
添加回復(fù) ({{post_count}})

相關(guān)推薦

快速安全登錄

使用微信掃碼登錄
{{item.label}} 加精
{{item.label}} {{item.label}} 板塊推薦 常見問題 產(chǎn)品動態(tài) 精選推薦 首頁頭條 首頁動態(tài) 首頁推薦
取 消 確 定
回復(fù)
回復(fù)
問題:
問題自動獲取的帖子內(nèi)容,不準確時需要手動修改. [獲取答案]
答案:
提交
bug 需求 取 消 確 定
打賞金額
當(dāng)前余額:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
請輸入 0.1-{{reward_max_price}} 范圍內(nèi)的數(shù)值
打賞成功
¥{{price}}
完成 確認打賞

微信登錄/注冊

切換手機號登錄

{{ bind_phone ? '綁定手機' : '手機登錄'}}

{{codeText}}
切換微信登錄/注冊
暫不綁定
CRMEB客服

CRMEB咨詢熱線 咨詢熱線

400-8888-794

微信掃碼咨詢

CRMEB開源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
返回頂部 返回頂部
CRMEB客服