以商品爲例
須要如下信息javascript
- 商品所在行的高度rowHeight(固定值)
- 商品的可視區域的高度contentHeight(半固定值,不考慮瀏覽器的resize)
- 可視區域距離視窗頂部的高度headHeight(固定值)
- content的滾動高度(與scroll事件相關,考慮到滑動快時不觸發上報,須要throttle)
/** * 滾動事件處理 * @param {number} headHeight content區域距離頂部的高度 * @param {number} rowHeight 每一行的高度 * @returns {Function} */ export function handleScroll(headHeight, rowHeight) { let lastActive = [] let deactived = [] /** * @param {number} contentTop 區域的top值 * @return {Array} 當前活躍的的行 */ return function(contentTop) { let topDiff = contentTop - headHeight // 可視區域高度 let visibleHeight = window.innerHeight - (topDiff <= 0 ? headHeight : contentTop) /** * 當前能顯示的行數 * 顯露一半就須要上報 則使用四捨五入 */ let rowCount = Math.round(visibleHeight / rowHeight) /** * 獲取當前顯示的下標 */ let index = topDiff > 0 ? 0 : Math.round(-topDiff / rowHeight) let _active = Array.from({ length: rowCount }).reduce( (pre, cur, i) => pre.concat(index + i), [] ) /** * 以前上報過,未從屏幕上消失過的 不上報 * 以前上報過,從屏幕中消失又出現的 上報 */ let active = _active.filter( v => !lastActive.includes(v) || deactived.includes(v) ) /** * 收集非活躍狀態的行,只收集滾上去的元素,active下面的行屬於待活躍狀態,因爲和行的總數相關(商品的總行數知道與否不影響上報),會額外增長沒必要要的工做 因此此處不作考慮 */ deactived = Array.from({ length: index }).map((val, i) => i) /** * 上次活躍的行,用來避免重複上報 */ lastActive = [].concat(deactived).concat(_active) return { lastActive, active, deactived } } }
let target = document.getElementById('wrapper') let onScroll = handleScroll(100, 420) let _scroll = _.throttle(function(){ let row = onScroll(target.getBoundingClientRect().y) // 此時row.active就是須要上報的行的下標,active可能爲空數組 ... }, 1000) target.addEventListener('scroll', _scroll)