国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

函數(shù)除顫/節(jié)流提高性能 + 原生實(shí)現(xiàn)滾動(dòng)時(shí)到視口時(shí)展現(xiàn)

reclay / 2786人閱讀

摘要:引言前端開發(fā)中一個(gè)老生常談的問題就是當(dāng)用戶滾動(dòng)時(shí)根據(jù)滾動(dòng)的位置適當(dāng)觸發(fā)不同的函數(shù)動(dòng)畫例如當(dāng)元素出現(xiàn)在視口時(shí)觸發(fā)該元素的改變通常的做法就是在上附加事件但是我們知道當(dāng)滾動(dòng)條滾動(dòng)時(shí)事件觸發(fā)的是很頻繁的且不由控制瀏覽器的事件隊(duì)列原生提供如圖添加事件

引言

前端開發(fā)中一個(gè)老生常談的問題就是"當(dāng)用戶滾動(dòng)時(shí), 根據(jù)滾動(dòng)的位置適當(dāng)觸發(fā)不同的函數(shù)/動(dòng)畫, 例如當(dāng)元素出現(xiàn)在視口時(shí)觸發(fā)該元素的style改變. 通常的做法就是在scrollElement上附加scoll事件. 但是我們知道, 當(dāng)滾動(dòng)條滾動(dòng)時(shí)scroll事件觸發(fā)的是很頻繁的, 且不由JS控制(瀏覽器的事件隊(duì)列原生提供), 如圖(添加事件監(jiān)聽后滾輪三格):

依據(jù)系統(tǒng)設(shè)置的不同, 一次滾輪觸發(fā)的scroll事件大概在10~15次之間. 如果在回調(diào)函數(shù)中添加大量的DOM操作或者計(jì)算的話, 會(huì)引起明顯的卡頓等性能問題. 那有沒有辦法去稀釋回調(diào)函數(shù)的觸發(fā)操作呢? 這個(gè)時(shí)候就需要函數(shù)節(jié)流(throttle)和debounce(去顫抖)來解決了!

2017-02-06更新函數(shù)式版本

這個(gè)版本運(yùn)用閉包封裝數(shù)據(jù), 修正this指向以加強(qiáng)魯棒性, 剔除了一開始就顯示在視口的元素
talk is cheap, here are the code

// 根據(jù)單一元素, throttle函數(shù)專門負(fù)責(zé)事件稀釋, 接受兩個(gè)參數(shù): 要間隔調(diào)用的函數(shù), 以及調(diào)用間隔. 
var throttle = function (fn, interval) {
  let start = Date.now()
  let first = true
  return function (...args) {
    let now = Date.now()
    // 如果是第一次調(diào)用, 則忽略時(shí)間間隔
    if (first) {
      fn.apply(this, args)
      first = false
      return
    }
    if (now - start > interval) {
      fn.apply(this, args)
      start = now
    }   
  }
}
// 顯示元素的IIFE
var showElems = (function (selector, func) {
  // 預(yù)處理, 標(biāo)識已經(jīng)顯示在視口的元素
  let elemCollect = [...document.querySelectorAll(selector)]
  let innerHeight = window.innerHeight
  let hiddenElems = []
  elemCollect.forEach((elem, index) => {
    let top = elem.getBoundingClientRect().top
    // 不顯示在視口才加入判斷隊(duì)列
    if (top > innerHeight) {
      hiddenElems.push(elem)
    }
  })
  // memory release
  elemCollect = null
  return function (...args) {
    hiddenElems.forEach((elem) => {
      let bottom = elem.getBoundingClientRect().bottom
      if (bottom < innerHeight) {
        func.apply(elem, args)
      }
    })
  }
})("p", function(e){
  console.log(this, e, "showed!")
})
// 組合, throttle函數(shù)負(fù)責(zé)稀釋showElems觸發(fā)的頻率, showElems負(fù)責(zé)元素滾動(dòng)到視口時(shí)的相應(yīng)動(dòng)作
var throttledScroll = throttle(showElems, 500)
window.addEventListener("scroll", throttledScroll)
debounce

設(shè)想一些用戶的頻繁操作, 例如滾動(dòng), 文本框輸入等, 每次觸發(fā)事件都要調(diào)用回調(diào)函數(shù); 這樣做的代價(jià)未免大了點(diǎn). 而debounce的作用就是在在用戶高頻觸發(fā)事件時(shí), 暫時(shí)不調(diào)用回調(diào), 在用戶停止高頻操作(例如停止輸入, 停止?jié)L動(dòng)時(shí)), 再調(diào)用一次回調(diào).

解決方案有了, 怎樣用代碼實(shí)現(xiàn)呢? 這里我們要用到setTimeout這個(gè)功能來做函數(shù)調(diào)用的延遲. 具體代碼如下(將代碼粘貼到console中執(zhí)行以下, 自己試試看):

            var timer;
            document.addEventListener("scroll", function(){
                clearTimeout(timer); //如果操作時(shí)已經(jīng)有了延遲執(zhí)行, 則取消該延遲執(zhí)行
                timer = setTimeout(function() { //設(shè)定新的延遲執(zhí)行
                    callback();
                }, 500)
            })

(這里我們?yōu)榱朔奖阏f明, 設(shè)定了timer全局變量. 實(shí)踐中我們可以將timer附加為函數(shù)的屬性, 隱藏在閉包中, 或者作為對象的屬性等. )

當(dāng)第一次高頻操作觸發(fā)時(shí), 設(shè)定一個(gè)timer, 在500ms后執(zhí)行; 如果用戶在500ms之內(nèi)沒有再次進(jìn)行該操作(本例中是滾動(dòng)), 那么我們調(diào)用callback; 然而如果500ms之內(nèi)用戶觸發(fā)了滾動(dòng)(即所謂的高頻操作), 那么我們清除上一次設(shè)定的timeout, 設(shè)定一個(gè)新的, 500ms之后執(zhí)行的timeout.

大家思考一下, debounce的本質(zhì)就是在用戶觸發(fā)expensive操作時(shí), 不斷延期該expensive操作的執(zhí)行時(shí)間(取消和設(shè)定timeout的代價(jià)是很小的). 當(dāng)用戶停止操作, 那我們就不再延期, 最后一次設(shè)定的timeout會(huì)在500ms后執(zhí)行expensive operation, 例如dom操作, 計(jì)算等.

到這里我們似乎已經(jīng)有了一個(gè)解決方案! 然而還有個(gè)小小的問題.....

如果用戶不停地操作, 那debounce就會(huì)不斷把操作延期, 如果用戶沒有兩次操作的間隔時(shí)間大于500ms, 那么我們的callback永遠(yuǎn)也得不到執(zhí)行. 可憐的callback! 恩, 在這一點(diǎn)上我們當(dāng)然可以改進(jìn)...

throttle

throttle的作用是, 保證一個(gè)函數(shù)至少間隔一段時(shí)間得到一次執(zhí)行. 不像等待用戶停止的debounce, throttle即使在用戶不停操作時(shí), 也能讓callback在操作期間得到間隔的執(zhí)行.

那么該怎么做呢? 一種方法是在用戶開始操作時(shí)記錄開始時(shí)間, 同時(shí)設(shè)定一個(gè)flag ifOperationBegin = true. 之后在每次用戶的操作中判斷當(dāng)前時(shí)間, 如果當(dāng)前時(shí)間-開始時(shí)間 > 某個(gè)值, 比如500ms, 則執(zhí)行callback, 同時(shí)設(shè)定ifOperationBegin = true, 以開始下一次的設(shè)定開始時(shí)間 -> 記錄操作時(shí)間 -> 判斷的循環(huán). 具體到代碼實(shí)現(xiàn)上:

            var scrollBegin = false, scrollStartTime = null; //用戶尚未開始操作
            document.addEventListener("scroll", function(){
                if(!scrollBegin)scrollStartTime = Date.now();//記錄開始時(shí)間, 前提是callback還沒有被觸發(fā)過
                scrollBegin = true;//設(shè)定flag
                if(Date.now() - scrollStartTime > 500){ //如果操作時(shí)間和開始時(shí)間間隔大于500ms則
                    exec(elems, cb); //調(diào)用回調(diào)
                    scrollBegin = false; //flag設(shè)為false, 以設(shè)定新的開始時(shí)間
                }
            })

這樣做的效果是, 在用戶持續(xù)觸發(fā)scroll操作時(shí), 保證在用戶操作期間callback至少會(huì)每隔500ms觸發(fā)一次. 如果用戶操作在500ms之內(nèi)結(jié)束, 那也木有關(guān)系, 下一次用戶重新開始操作時(shí)我們的scrollStartTime 依然保留著, callback會(huì)被立即觸發(fā).

實(shí)際運(yùn)用

那這兩種技術(shù)可以運(yùn)用到哪里呢? 請看如下代碼栗子:

        function detectVisible(selector, cb, interval){ //檢測元素是否在視口的函數(shù)
            var elems = document.querySelectorAll(selector), innerHeight = window.innerHeight;
            var exec = function(elems, cb){ //回調(diào)函數(shù)
                Array.prototype.forEach.call(elems, function(elem, index){
                    if(elem.getBoundingClientRect().top < innerHeight){ //判斷元素是否出現(xiàn)在視口
                        cb.call(elem, elem); //調(diào)用傳入的回調(diào)
                    }
                })
            }
            document.addEventListener("scroll", function(){ //使用debounce和throttle來稀釋scroll事件
                clearTimeout(detectVisible.timer);
                if(!detectVisible.scrollBegin)detectVisible.scrollStartTime = Date.now();
                detectVisible.scrollBegin = true;
                if(Date.now() - detectVisible.scrollStartTime > interval){
                    exec(elems, cb);
                    console.log("invoked by throttle!")
                    detectVisible.scrollBegin = false;
                }
                detectVisible.timer = setTimeout(function() {
                    exec(elems, cb);
                    console.log("invoked by debounce!")
                }, interval)
            })
        }
        detectVisible("div.elem", function(elem){
            this.style.backgroundColor = "yellow";
        }, 500);

這個(gè)栗子中我們綜合運(yùn)用了throttledebounce, 達(dá)到了如下效果: 用戶不停滾動(dòng)時(shí)callback會(huì)至少每500ms觸發(fā)一次; 用戶停止?jié)L動(dòng)后的500ms判斷函數(shù)也會(huì)觸發(fā)一次. 大家可以打開console查看callback何時(shí)是被throttle觸發(fā)的, 何時(shí)是被debounce觸發(fā)的.

總結(jié)

這一篇文章的主要主題事件的稀釋以期性能上的改善, 有兩種解決方法:throttledebounce. 前者是通過在用戶操作時(shí)判斷操作時(shí)間, 來達(dá)到間隔一段時(shí)間觸發(fā)回調(diào)的效果; 而后者則是將觸發(fā)的時(shí)間不斷延期, 直到用戶停止操作再執(zhí)行回調(diào). 兩者各有優(yōu)缺點(diǎn), 兩相結(jié)合, 我們得到了一個(gè)用戶無論怎樣操作(不停操作或者操作時(shí)間極短)都可以保證callback定期得到執(zhí)行的函數(shù). problem solved!

看完這篇, 如果你有所收獲, 請去github給我加個(gè)star唄!~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/50227.html

相關(guān)文章

  • 函數(shù)除顫/節(jié)流提高性能 + 原生實(shí)現(xiàn)滾動(dòng)時(shí)視口時(shí)展現(xiàn)

    摘要:引言前端開發(fā)中一個(gè)老生常談的問題就是當(dāng)用戶滾動(dòng)時(shí)根據(jù)滾動(dòng)的位置適當(dāng)觸發(fā)不同的函數(shù)動(dòng)畫例如當(dāng)元素出現(xiàn)在視口時(shí)觸發(fā)該元素的改變通常的做法就是在上附加事件但是我們知道當(dāng)滾動(dòng)條滾動(dòng)時(shí)事件觸發(fā)的是很頻繁的且不由控制瀏覽器的事件隊(duì)列原生提供如圖添加事件 引言 前端開發(fā)中一個(gè)老生常談的問題就是當(dāng)用戶滾動(dòng)時(shí), 根據(jù)滾動(dòng)的位置適當(dāng)觸發(fā)不同的函數(shù)/動(dòng)畫, 例如當(dāng)元素出現(xiàn)在視口時(shí)觸發(fā)該元素的style改變....

    mengera88 評論0 收藏0
  • 圖片懶加載的前世今生

    摘要:配置項(xiàng)配置項(xiàng)中的參數(shù)有以下三個(gè)所監(jiān)聽對象的具體祖先元素,默認(rèn)是計(jì)算交叉狀態(tài)時(shí),將附加到祖先元素上,從而有效的擴(kuò)大或者縮小祖先元素判定區(qū)域設(shè)置一系列的閾值,當(dāng)交叉狀態(tài)達(dá)到閾值時(shí),會(huì)觸發(fā)回調(diào)函數(shù)。 一、前言 ??通常情況下,HTML 中的圖片資源會(huì)自上而下依次加載,而部分圖片只有在用戶向下滾動(dòng)頁面的場景下才能被看見,否則這部分圖片的流量就白白浪費(fèi)了。 ??所以,對于那些含有大量圖片資源的網(wǎng)...

    zhaot 評論0 收藏0
  • 移動(dòng)端滾動(dòng)研究

    摘要:還會(huì)有一個(gè)性能上的問題就是當(dāng)頁面的列表過長,元素過多時(shí),在模擬滾動(dòng),下拉刷新這段時(shí)間內(nèi),頁面也會(huì)有卡頓現(xiàn)象,這里采取了一個(gè)優(yōu)化策略即列表較長時(shí)數(shù)量較多時(shí),在觸發(fā)下拉刷新的時(shí)機(jī)時(shí)將頁面視窗之外的元素隱藏或者存放在里面。 移動(dòng)web滾動(dòng)問題 在移動(dòng)端如果使用局部滾動(dòng),意思就是我們的滾動(dòng)在一個(gè)固定寬高的div內(nèi)觸發(fā),將該div設(shè)置成overflow:scroll/auto;來形成div內(nèi)部的...

    ghnor 評論0 收藏0
  • 【前端詞典】5 種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較[性能升級版]

    摘要:用于獲得當(dāng)前元素到定位父級頂部的距離偏移值。后來在項(xiàng)目中總會(huì)遇到滾動(dòng)吸頂?shù)男Ч枰獙?shí)現(xiàn),現(xiàn)在我將我知道的種滾動(dòng)吸頂實(shí)現(xiàn)方式做詳細(xì)介紹。有兼容性問題,在微信瀏覽器某些版本中的值會(huì)為,于是乎也就有了第三種方案的兼容性寫法。修改版預(yù)覽 這篇文章是三天前寫就的,有大佬給我提了一些修改意見,我覺得這個(gè)意見確實(shí)中肯。所以就有了這個(gè)升級的修改版本。代碼同步更新到 GitHub 了。 修改內(nèi)容如下: 添加...

    happyfish 評論0 收藏0
  • 延遲加載(Lazyload)三種實(shí)現(xiàn)方式

    摘要:原文鏈接延遲加載也稱為惰性加載,即在長網(wǎng)頁中延遲加載圖像。傳入一個(gè)回調(diào)函數(shù),當(dāng)其觀察到元素集合出現(xiàn)時(shí)候,則會(huì)執(zhí)行該函數(shù)。管理的是一個(gè)數(shù)組,當(dāng)元素出現(xiàn)或消失的時(shí)候,數(shù)組添加或刪除該元素,并且執(zhí)行該回調(diào)函數(shù)。 原文鏈接 - https://zhuanlan.zhihu.com/p/25455672 延遲加載也稱為惰性加載,即在長網(wǎng)頁中延遲加載圖像。用戶滾動(dòng)到它們之前,視口外的圖像不會(huì)加載。...

    niuxiaowei111 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<