纯CSS技术实现Scroll Indicator滚动指示器效果
最近在读阮一峰老师的的《ECMAScript 6 入门》,发现每个章节的页面顶部都有一个类似进度条的东西,这是个什么东东,好奇心强的我上网查了一下,这个东西叫 Scroll Indicator。
Scroll Indicator:滚动指示器。是 Web 中常见的一种效果。用户滚动垂直滚动内容时,页面顶部有一个类似进度条的效果,当内容滚动到页面最低端,进度条效果填满整个进度条。通俗来说,就是当前可视区域距离页面顶部的占比,感觉描述得有点绕,还是录制一个动效图,用图来说明这种效果,毕竟一图胜过千言万语:
JavaScript 实现
在还没有捞源码之前,自己先大致想了一下实现思路:
- 页面加载完成之后,获取到页面文档高度(DH)、当前可视区域高度(VH)、可视区域距离页面顶部的高度 scrollTop(TH);
- (DH-VH)就是需要滚动的值;
- 监听页面 scroll 事件,(TH/(DH-VH))*100%,便是当前的占比;
- 当然,需要考虑节流、防抖。推荐阅读《第 6 题:什么是防抖和节流 有什么区别 如何实现》
HTML 代码:
<body> <div class="scroll-indicator j-scroll-indicator"></div> </body>
css 代码:
body { margin: 0; paddingL 0; height: 5000px; } .scroll-indicator { position: fixed; height: 8px; top: 0; left: 0; background: #369; }
核心代码:
(function() { var dh = $(document).height(); var vh = $(window).height(); var sHeight = dh - vh; $(window).scroll(function () { var perc = $(window).scrollTop() / (dh - vh); $('.j-scroll-indicator').css({width: perc * 100 + '%'}); }); }());
效果如下:
然后去捞了一下阮老师 es6 官网的源码:
(function() { var $w = $(window); var $prog2 = $('.progress-indicator-2'); var wh = $w.height(); var h = $('body').height(); var sHeight = h - wh; $w.on('scroll', function() { window.requestAnimationFrame(function(){ var perc = Math.max(0, Math.min(1, $w.scrollTop() / sHeight)); updateProgress(perc); }); }); function updateProgress(perc) { $prog2.css({width: perc * 100 + '%'}); ditto.save_progress && store.set('page-progress', perc); } }());
不出所料,做法是一样的。
然后,就开始瞎琢磨,之前用 css 搞过瀑布流,那可以用 css 搞滚动指示器吗?
答案当然是可以的。
我想到的更好的实现
我想到的这个技术实现区别就在于对角线性渐变不是写在 body 标签上的,而是一个普通的 div 元素上。
具体操作如下:
1、在
标签内插入指示器元素:<div class="indicator"></div>
2、粘贴如下所示的 CSS 代码:
body { position: relative; } .indicator { position: absolute; top: 0; right: 0; left: 0; bottom: 0; background: linear-gradient(to right top, teal 50%, transparent 50%) no-repeat; background-size: 100% calc(100% - 100vh); z-index: 1; pointer-events: none; mix-blend-mode: darken; } .indicator::after { content: ''; position: fixed; top: 5px; bottom: 0; right: 0; left: 0; background: #fff; z-index: 1; }
一个更好的 CSS 滚动指示器效果就实现了
原理说明
传统 CSS 滚动指示器为了防止对角渐变(也就是滚动进度条)的覆盖页面上的元素内容,因此写在了最底层的 body 元素上,这就导致如果 body 元素内的普通元素内容有背景色,或者背景图之类的,就会覆盖进度条,产生致命缺陷。
我的优化方法是把对角渐变(也就是滚动进度条)连同里面的白色覆盖层写在了普通元素的上面,这样避开被覆盖的致命缺陷。但是这样实现带来另外一个问题,页面的内容都被白色图层覆盖了,那页面内容岂不是都看不见了?不要担心,有 CSS 声明可以让白色的图层变成透明,那就是 mix-blend-mode:darken,也就是 darken 混合模式。darken 混合模式的混合方式很好理解,两个颜色进行混合,哪个颜色深就使用哪个颜色?
要知道所有的颜色里面最浅的就是白色,于是我们只要把我们的白色覆盖层的混合模式设置为 darken,那必然最终呈现出来的颜色一定是覆盖层下面元素内容的颜色,换句话说我们的白色透明覆盖层变透明了。
CSS 滚动指示器要想效果良好,需要注意两点:
- 进度条的颜色尽量取深色,因为本身包含 darken 混合模式,如果颜色过浅,很容易被底部的内容颜色给混合。
- CSS 滚动指示器需要在页面滚动高度超过一屏的时候才出现。原因有两方面:一是如果滚动高度过小,没有必要使用滚动指示器;二是滚动指示器本质上是一个渐变,如果滚动高度不足,则进度条的边缘会过于倾斜而导致显示效果不完美。
网友的方法也不错
使用 CSS 来实现滚动指示器,难点在于:如何实时去获取当前可视区域在文档流中的位置。
上述问题,如何去考虑呢?我们从分解滚动指示器的动作中来找答案:
- 黑色表色进度条
- 红色表示文档高度
- 绿、蓝、橙表示视窗滚动
也就是说:(TH/(DH-VH))公式中的 TH 可以不用知道,只需要 DH-VH 的高度,即直角三角形的高度便 OK。
现在问题转化为:如何求出 VH,这时候该 vh 这个长度单位登场了,vh 是基于视窗单位的排版计量。vh 可以获取当前视窗的高度。嗯,现在看来,应该是可以一写了。
HTML 代码:
<body> <div class="main"> <h1>滚动鼠标</h1> </div> </body>
CSS 代码:
.main { margin: 0; padding: 0; display: block; height: 30000px; text-align: center; line-height: 100px; } body { margin: 0; padding: 0; background: linear-gradient(to right top, #369 50%, #fff 50%); background-size: 100% calc(100% - 99vh); } body:before { content: ''; position: fixed; top: 4px; bottom: 0; width: 100%; z-index: -1; background: #fff; }
大致实现是没有问题的,但是有下面几个缺点:
- 文档内容太少(高度太小)的话,进度条呈箭头形,不美观(可考虑加毛玻璃效果来弱化)
- background-size:100%calc(100%-99vh);中的 99vh 是相对值,若是视窗高度比较小,进度条会填不满进度条槽(可考虑加 min-height 来弱化)
结束语
其实这只是非常牛逼的渐变非常小的一个技巧,更多精彩 CSS 技术文章敬请关注码云笔记,持续更细。
好了,本文到此结束,希望对你有帮助,如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
码云笔记 » 纯CSS技术实现Scroll Indicator滚动指示器效果