以商品为例
须要如下信息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)