背景
为了良好的用户体验,需要在容器顶部可滚动的情况下增加一个阴影条,提示用户可向上滚动;如果容器顶部不可滚动,则不显示阴影条,效果如下图所示:
实现
首先我们定义好dom和相应的样式:
html
<!-- 容器 -->
<div class="container">
<!-- 阴影 -->
<div class="shadow"></div>
<!-- 占位用,使容器能够滚动 -->
1<br/>
2<br/>
3<br/>
4<br/>
5<br/>
6<br/>
7<br/>
8<br/>
9<br/>
10<br/>
11<br/>
12<br/>
13<br/>
14<br/>
15<br/>
16<br/>
</div>
css
// 给容器设置最大高度,使其产生滚动
.container {
max-height: 200px;
overflow-y: auto;
border: 1px solid black;
position: relative;
}
// 利用sticky定位实现吸顶效果
.shadow {
position: sticky;
visibility: hidden;
top: 0;
left: 0;
right: 0;
box-shadow: 0 10px 0 10px rgba(0, 0, 0, 0.2);
// 如果不希望阴影挡住下方内容的点击事件的话
pointer-events: none;
}
核心的实现便是如何判断容器顶部是否还有滚动空间,我们用到scrollTop属性,如果scrollTop大于0,说明可以向上滚动:
js
import { debounce } from 'https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js';
// 容器
const container = document.querySelector('.container');
// 阴影
const shadow = document.querySelector('.shadow');
// 用到了lodash的防抖函数,
// 通过scrollTop是否大于0来判断容器顶部是否还有滚动空间,
// 从而控制shadow是否显示。
const handler = debounce((e) => {
if (e.target.scrollTop > 0) {
shadow.style.visibility = 'visible';
} else {
shadow.style.visibility = 'hidden'
}
}, 50, { leading: true });
// 监听滚动事件
container.addEventListener('scroll', handler);
然后就实现了开头的滚动阴影效果。
举一反三
会了顶部阴影,那怎么实现底部、左侧、右侧阴影呢?聪明的你肯定脱口而出用scrollBottom、scrollLeft、scrollRight比较一下就行了。行吗?不太行哦,元素没有scrollBottom、
scrollRight属性,因此底部和右侧阴影不能用这个方法实现。那么应该怎么搞呢,也简单,用到元素的scrollWidth和scrollHeight就可以实现scrollBottom和scrollRight的效果:
js
const scrollRight = scrollWidth - scrollLeft;
const scrollBottom = scrollHeight - scrollTop;
// 然后再使用前文的逻辑处理即可
......
// 但是这里还有个坑,
// 上述的scrollBottom和scrollRight是通过浮点数相减来的,
// 会有精度问题,因此比较时不是直接与0比较,而是用Number.EPSILON
// 举个栗子
scrollRight > Number.EPSILON
其实不止滚动阴影,还有一些效果也是可以用这个思路做的,比如下图中的指示器效果:
总结
写篇小水文,混混经验。我发现长篇的干货数据不太好,反而是这样几分钟看完的小水文数据还挺好看的。虽然水,但是也有不少知识点:
- sticky定位实现吸顶效果
- pointer-events实现鼠标事件穿透
- 判断元素某个方向是否可滚动
- 防抖函数以及立即执行
- 浮点数精度用EPSILON处理
就是这样了,完。