[JavaScript 笔记] 自己动手实现平滑滚动条
Dec 20, 2017
前期调研
当前浏览器的标准也越来越明确, 用户体验越来越重要. 相比于FireFox, Chrome没有自带平滑滚动, 鼠标滚轮滚动时直接滚动到指定位置, 没有实现缓动, 这应该是出于性能的考虑. 那么, 如何手工去实现平滑滚动, 或者说是带动画的滚动呢?
首先, Mark几个不错的开源项目, 打开传送门
- No1. nicescroll : 平滑滚动的jQuery插件, 兼容性非常不错
- No2. fullPage : 整页滚动jQuery插件, 适合极简风格的介绍型页面
- No3. scrollTo : 平滑滚动的jQuery插件+1, 非常常用且轻量
其实, 手动实现一个这样的效果并不困难, 只要做以下两件事即可
- 截获mousewheel事件, 阻止浏览器默认行为
- 按照事件参数处理滚轮事件, 并通过动画的形式实现
踩坑和踩坑
浏览器兼容性
不同浏览器对滚轮事件的绑定都是不一样的, 比如
- IE下是这样的:
1
2
3if(document.attachEvent){
document.attachEvent('onmousewheel',smoothBar);
} - FF下是这样的(FF自带了平滑滚动, 实际情况下无需绑定FF的滚轮事件)
1
2
3if(document.addEventListener){
document.addEventListener('DOMMouseScroll',smoothBar,false);
} - Chrome/Safari下是这样的
1
window.onmousewheel=document.onmousewheel=smoothBar;
还是浏览器的兼容性问题
稍低版本的IE浏览器事件没有target属性, 也没有preventDefault函数
这里是一个简单的fix方法1
2
3
4
5
6function(eventToFix) {
if (eventToFix && eventToFix.target) return eventToFix;
eventToFix=eventToFix|| window.event;
eventToFix.preventDefault = function() { this.returnValue = false; };
return eventToFix;
}仍然是浏览器的兼容性
设置或获取当前滚动位置在不同浏览器也是不同的, 下面这句话能够兼容的获取滚动位置其中, document.pageYOffset是Safari专用的.1
2
3var scrollTop = document.documentElement.scrollTop ||
window.pageYOffset ||
document.body.scrollTop;
其他的坑比如IE9以下还没有requestAnimationFrame函数等等, 浏览器兼容性是个超大的坑, 在IE下没能正常跑出来, 最后其实做的是一个Chrome下的原生JS平滑滚动, 不支持IE和FF.
如何判断元素是否可滚动
这是关键性的问题, 当截流了所有的mousewheel事件后, 一个页面可能有很多scrollbar, 如何根据截取的事件判断应该让哪个元素滚起来呢?
大概逻辑是这样的:
- 如果event.target是body元素, 直接滚body
- 如果event.target是其他元素, 判断这个元素能不能滚, 如果不能, 判断父节点能不能滚直到找到滚的起来的或body元素
参考了一些资料, 发现有两种判断方式:
- 只要element.scrollHeight > element.clientHeight, 说明是个能滚的元素
- 见下图
这两种都是不准确的, scrollHeight > clientHeight不一定是有滚动条, 可能有其他原因, 具体原因尚待验证,
其次overflow:visible的元素也可能是有滚动条的
1 | /* 原生JS代码 */ |
实践与结果
实现思路已经很明确, 截取滚轮事件和判断元素是否可滚动已经理解, 只欠把对应的元素用动画滚起来了.
前端实现动画有几种方法:
- css3 transition动画或animation+keyframes实现, css并不支持scrollTop属性的动画, 支持动画的css属性有这些
- jQuery插件实现, 不用自己造轮子
- 使用requestAnimationFrame函数自己写一个高性能的平滑滚动, 享受造轮子的乐趣
代码如下~
1 | window.onmousewheel=document.onmousewheel=smoothBar; |