摘要:前言現在網上下拉刷新,上拉加載插件一搜一大堆,如果你想用在生產環境,那你可以直接網上搜一個靠譜的,我所做的就是不依賴任何插件,一步一步把這個插件的過程寫一下,各位同學可以在此基礎上定制,沒有寫過插件的,可以了解下插件怎么寫的,整個過程定位入
前言
現在網上 下拉刷新,上拉加載 插件一搜一大堆,如果你想用在生產環境,那你可以直接網上搜一個靠譜的,我所做的就是不依賴任何插件,一步一步把這個插件的過程寫一下,各位同學可以在此基礎上定制,沒有寫過插件的,可以了解下插件怎么寫的,整個過程定位入門級,看不懂?那么百度插件直接用就好了,修煉一段時間在考慮怎么寫插件吧。廢話不多說,上效果圖
下拉刷新的原理就是,添加一個 div,這個 div 里面標識加載的過程,根據拉下的距離改變div的高度,從而顯示 下拉加載 的文字,松手后根據下拉的距離,判斷是否請求數據
構建過程定義默認的一些配置項
var defaults = { threshold: 100, // 滑動觸發下拉刷新的距離 stop: 40, // 下拉刷新時停留的位置距離屏幕頂部的距離 dis: 20 // 距離屏幕底端觸發 上拉加載 的距離 }
定義構造函數
function JrRefresh (el, options) { this.options = Object.assign({}, defaults, options) // 合并參數 this.el = typeof el === "string" ? document.querySelector(el) : el // 定義要操作對象 this.progress = null // 下拉刷新顯示的 dom this.loadMore = null // 上拉加載顯示的 dom this.progressHeight = 0 // 下拉刷新的 dom 的高度 this.rotate = null // 下拉刷新 轉圈 的角度 this.touchstartY = 0 // 觸摸到屏幕的坐標起始 Y 值 this.currentY = 0 // 移動時實時記錄的坐標 Y 值 this.isAnimation = false // 是否在自動回滾 this.isRefresh = false // 是否正在刷新數據 this.isLoadMore = false // 是否正在加載數據 this.hasMore = true // 是否有更多數據, 下拉加載會用 this.rotateTimer = null // 控制 下拉刷新 轉圈 的時間計時器 this.event() this.init() }
初始化,添加一些需要用到的 dom 元素
JrRefresh.prototype.init = function () { // 增加下拉刷新的顯示 var refreshHtml = `` var divm = document.createElement("div") divm.innerHTML = refreshHtml this.progress = divm.children[0] this.el.prepend(this.progress) // 增加上拉加載的顯示 var loadMoreHtml = `下拉刷新
` var div = document.createElement("div") div.innerHTML = loadMoreHtml this.loadMore = div.children[0] this.el.appendChild(this.loadMore) }加載中...
定義事件,解釋一下這里用到的 bind 吧,這里的 JrRefresh.prototype.pullDown、JrRefresh.prototype.touchend 等函數里的 this 綁定到 JrRefresh 的構造函數上, 如果不用的話,pullDown 函數是由 this.el 調用的,this 會指向 this.el。(假裝你們都懂了?)這是寫插件常用到一種綁定 this 的方法之一
JrRefresh.prototype.event = function () { this.el.addEventListener("touchstart", this.handleTouchStart.bind(this)) this.el.addEventListener("touchmove", this.pullDown.bind(this)) this.el.addEventListener("touchend", this.touchend.bind(this)) window.addEventListener("scroll", this.handleScroll.bind(this)) }
手指觸摸時記錄Y值坐標,用來判斷是向上滑還是向下
JrRefresh.prototype.handleTouchStart = function (e) { // 記錄手指觸摸屏幕的 Y 值坐標 this.touchstartY = e.changedTouches[0].clientY }
手指開始滑動時,觸發 pullDown 事件,用 this.currentY 來記錄實時的 Y 值坐標,當 this.currentY - this.touchstartY > 0 時,說明是手指向下滑動,這里面 e.preventDefault() 是為了防止微信還有 ios 的瀏覽器下拉時頂部出現出現默認的黑色空白,或者觸發 uc 等瀏覽器自帶的下拉刷新, 由 this.moveDown 專門來控制 下拉刷新顯示的dom(this.progress) 的高度
JrRefresh.prototype.pullDown = function (e) { var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop this.currentY = e.targetTouches[0].clientY if (this.currentY - this.touchstartY >= 0 && scrollTop <= 0) { e.preventDefault() if (!this.isAnimation && !this.isRefresh) { this.moveDown(this.currentY - this.touchstartY) } } }
moveDown 函數是用來專門控制 this.progress 的高度,rotateProgress 函數用來專門控制圈圈的旋轉角度 changeProgressState 函數用來專門控制this.progress 顯示內容
JrRefresh.prototype.moveDown = function (dis) { if (dis < this.options.threshold) { this.progress.style.height = this.progressHeight + dis + "px" this.rotateProgress(dis*2) this.changeProgressState("下拉刷新") } else { // 當滑動距離超過 threshold 時,放慢下拉速度 var aftDis = this.options.threshold + (dis - this.options.threshold) / 3 var aftAngle = this.options.threshold * 2 + (dis - this.options.threshold) / 1.7 this.progress.style.height = this.progressHeight + aftDis + "px" this.rotateProgress(aftAngle) this.changeProgressState("釋放刷新") } }
圈圈只有兩種情況,一種隨手指移動,超過距離轉圈速度變慢,一種是自己不停轉,前一種情況,手指下滑距離跟旋轉角度的比例并不一定按我這個寫法,主要有一點,就是轉動變慢時候,圈圈旋轉角度不要出現突變,這也是這個插件的難點之一,不懂的同學多看多想哈
JrRefresh.prototype.rotateProgress = function (rotate) { var rotateDom = this.progress.querySelector(".downwarp-progress") if (rotate != undefined) { rotateDom.style.transform = "rotate(" + rotate + "deg)" this.rotate = rotate } else { var t = 0; this.rotateTimer = setInterval(() => { t++ var angle = (this.rotate + t*15) % 360 rotateDom.style.transform = "rotate(" + angle + "deg)" rotateDom.style.WebkitTransform = "rotate(" + angle + "deg)" }, 16) } }
changeProgressState 這個函數沒什么好說的了
JrRefresh.prototype.changeProgressState = function (name) { this.progress.querySelector(".downwarp-tip").innerHTML = name }
至此,下滑效果有點樣子了,下面是手指松開時候的邏輯
JrRefresh.prototype.touchend = function () { var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop if (scrollTop > 0 || this.isRefresh|| this.isAnimation) return //只有 1.在屏幕頂部 2.已完成請求數據 3.不在回滾 三條都滿足才進行處理 if ((this.currentY - this.touchstartY) > this.options.threshold) { this.options.downCallback() // 觸發參數穿過來的請求數據 this.isRefresh = true this.moveBack(this.options.stop) // 下拉刷新時停留的位置距離屏幕頂部的距離 } else { this.moveBack() } }
moveBack 函數專門把進度返回到對應位置
JrRefresh.prototype.moveBack = function (dis) { var dis = dis || 0; this.isAnimation = true // 正在回退 var currentHeight = this.progress.offsetHeight var t = 0, // 進行的步數 b = 10, // 總步數 c = (currentHeight - dis)/b // 每一步的距離 var timer = setInterval(() => { t++; this.progress.style.height = currentHeight - c * t + "px" if (t == b) { if (dis === 0) { this.changeProgressState("下拉刷新") this.progressHeight = 0 } else { this.changeProgressState("正在刷新") this.progressHeight = this.options.stop this.rotateProgress() } this.touchstartY = "" this.isAnimation = false // 回退完成 clearInterval(timer) } }, 16) }
當請求數據完成,要回滾到原始位置,參數是 boolean 類型,表明有沒有更多數據
JrRefresh.prototype.endSuccess = function (bool) { if (this.isRefresh) { // 如果是正在刷新數據 this.changeProgressState("刷新成功") if (bool) { setTimeout(() => { //延遲 500ms 回滾 this.moveBack(0) this.isRefresh = false clearInterval(this.rotateTimer) },500) } else { this.toggleLoadingText(true) } } if (this.isLoadMore) { // 如果是正在加載數據 this.isLoadMore = false this.loadMore.style.visibility = "hidden" this.toggleLoadingText(bool) } } JrRefresh.prototype.toggleLoadingText = function (hasMore) { if (hasMore) { this.loadMore.querySelector(".upwarp-tip").innerHTML = "加載中..." this.loadMore.querySelector(".upwarp-progress").style.display = "inline-block" } else { this.loadMore.style.visibility = "visible" this.loadMore.querySelector(".upwarp-tip").innerHTML = "沒有更多數據了" this.loadMore.querySelector(".upwarp-progress").style.display = "none" } }
至此,下拉刷新的邏輯終于完成了,下面開始上拉加載,比較麻煩的是獲取頁面高度,數據過來的同時請求頁面高度,頁面還沒有渲染完成,頁面高度可能會不準,所以我處理方法是延遲100ms 獲取高度,
JrRefresh.prototype.handleScroll = function () { var top = this.loadMore.getBoundingClientRect().top; // 獲取最底部標簽距離屏幕頂部的距離 if (top + 10 < window.innerHeight && !this.isLoadMore && this.hasMore) { this.isLoadMore = true this.loadMore.style.visibility = "visible" this.options.up.callback() } }用法
//列表內容,如:列表數據
..
好了,大致就介紹這么多,想看源碼的同學,移步這里,后續的完善和更新也會在 github 上,有興趣的同學 star 或者 fork 哦
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51797.html
摘要:前言現在網上下拉刷新,上拉加載插件一搜一大堆,如果你想用在生產環境,那你可以直接網上搜一個靠譜的,我所做的就是不依賴任何插件,一步一步把這個插件的過程寫一下,各位同學可以在此基礎上定制,沒有寫過插件的,可以了解下插件怎么寫的,整個過程定位入 前言 現在網上 下拉刷新,上拉加載 插件一搜一大堆,如果你想用在生產環境,那你可以直接網上搜一個靠譜的,我所做的就是不依賴任何插件,一步一步把這個...
摘要:是一個移動端的上拉下拉加載更多的組件。因為在節點元素創建之前,必須先設定高度,否則會導致無法滾動創建完畢是指定給第一個子元素滾動,所以的上拉和下拉刷新也是追加到第一個子元素里面,其實把第一個子元素想象成為里面的就可以了。 listloading.js listloading是一個移動端的上拉、下拉加載更多的組件。主要依賴于iscroll.js v5.1.2基礎上開發的組件,基礎庫可以使...
閱讀 2602·2021-11-17 09:33
閱讀 3949·2021-10-19 11:46
閱讀 914·2021-10-14 09:42
閱讀 2258·2021-09-22 15:41
閱讀 4221·2021-09-22 15:20
閱讀 4636·2021-09-07 10:22
閱讀 2308·2021-09-04 16:40
閱讀 819·2019-08-30 15:52