6.2 改造 PC 端滚动事件
通过上述说明,我们已经知道截流函数可以通过限定滚动事件触发频率提升性能,同时,设置在 延迟时间开始阶段立即调用滚动事件的回调函数 并不会牺牲用户体验。
截流函数上文已经定义好,使用起来就很简单了:
// 设置截流函数 let handleMouseWheel = utils.throttle(this.scrollMouse, this, this.DELAY, true); // 鼠标滚轮监听,火狐鼠标滚动事件不同其他 if (navigator.userAgent.toLowerCase().indexOf('firefox') === -1) { document.addEventListener('mousewheel', handleMouseWheel); } else { document.addEventListener('DOMMouseScroll', handleMouseWheel); }
上面这部分代码是写在 class 的 init 方法中,所以截流函数的上下文(context)传入的是 this ,表示当前 class 实例。
7)其他
7.1 导航按钮
为了简化 html 结构,导航按钮通过 js 创建。这里的难点在于 如何实现点击不同按钮实现对应页面的跳转并更新对应按钮的样式 。
解决的思路是:
页面跳转:页面个数和导航按钮的个数一致,所以点击第 i 个按钮也就是跳转到第 i 个页面,而第 i 个页面对应的容器 top 值恰好是 -(i * this.viewHeight)
更改样式:更改样式即先删除所有按钮的选中样式,然后给当前点击的按钮添加选中样式。
// 创建右侧点式导航 createNav() { const nav = document.createElement('p'); nav.className = 'nav'; this.container.appendChild(nav); // 有几页,显示几个点 for (let i = 0; i < this.pagesNum; i++) { nav.innerHTML += '<p class="nav-dot"><span></span></p>'; } const navDots = document.querySelectorAll('.nav-dot'); this.navDots = Array.prototype.slice.call(navDots); // 添加初始样式 this.navDots[0].classList.add('active'); // 添加点式导航点击事件 this.navDots.forEach((el, i) => { el.addEventListener('click', event => { // 页面跳转 this.currentPosition = -(i * this.viewHeight); this.turnPage(this.currentPosition); // 更改样式 this.navDots.forEach(el => { utils.deleteClassName(el, 'active'); }); event.target.classList.add('active'); }); }); }
7.2 自定义参数
得当的自定义参数可以增加插件的灵活性。
参数通过构造函数传入,并通过 Object.assign() 进行参数合并:
constructor(options) { // 默认配置 const defaultOptions = { isShowNav: true, delay: 150, definePages: () => {}, }; // 合并自定义配置 this.options = Object.assign(defaultOptions, options); }
7.3 窗口尺寸改变时更新数据
浏览器窗口尺寸改变的时候,需要重新获取可视区、页面元素高度,并重新确定容器当前的 top 值。
同时,为了避免不必要的性能开支,这里使用了防抖动函数。
// window resize 时重新获取位置 getNewPosition() { this.viewHeight = document.documentElement.clientHeight; this.container.style.height = this.viewHeight + 'px'; let activeNavIndex; this.navDots.forEach((e, i) => { if (e.classList.contains('active')) { activeNavIndex = i; } }); this.currentPosition = -(activeNavIndex * this.viewHeight); this.turnPage(this.currentPosition); } handleWindowResize(event) { // 设置防抖动函数 utils.debounce(this.getNewPosition, this, event, this.DELAY); } // 窗口尺寸变化时重置位置 window.addEventListener('resize', this.handleWindowResize.bind(this));
7.4 兼容性
这里的兼容性主要指两个方面:一是不同浏览器对同一行为定义了不同 API,比如上文提到的获取鼠标滚动信息的 API Firefox 和其他浏览器不一样;第二点就是 ES6 新语法、新 API 的兼容处理。
对于 class、箭头函数这类新语法的转换,通过 babel 就可完成,鉴于本插件代码量很小,都处于可控的状态,并没有引入 babel 提供的 polyfill 方案,因为新 API 只有 Object.assign() 需要做兼容处理,单独写个 polyfill 就好,如下:
// polyfill Object.assign polyfill() { if (typeof Object.assign != 'function') { Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { if (target == null) { throw new TypeError('Cannot convert undefined or null to object'); } let to = Object(target); for (let index = 1; index < arguments.length; index++) { let nextSource = arguments[index]; if (nextSource != null) { for (let nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true, }); } },
引用自: MDN-Object.assign()
因为本插件只兼容到 IE10,所以不打算对事件做兼容处理,毕竟IE9 都支持 addEventListener 了。
7.5 通过惰性载入进一步优化性能
在 5.1 中写的 getWheelDelta 函数每次执行都需要检测是否支持 event.wheelDelta ,实际上,浏览器只需在第一次加载时检测,如果支持,接下来都会支持,再做检测是没必要的。
并且这个检测在页面的生命周期中会执行很多次,这种情况下可以通过 惰性载入 技巧进行优化,如下:
getWheelDelta(event) { if (event.wheelDelta) { // 第一次调用之后惰性载入,无需再做检测 this.getWheelDelta = event => event.wheelDelta; // 第一次调用使用 return event.wheelDelta; } else { // 兼容火狐 this.getWheelDelta = event => -event.detail; return -event.detail; } },
相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!
推荐阅读:
使用JS判断字符串中包含内容方法总结
JS+HTML5实绑定鼠标事件的粒子动画