界面无滚动条
滚动条的优化也有很多种只有进程没有界面,比如随便再网上搜索美化浏览器滚动条样式,就会出现些用css去美化滚动条的方案。
那种更好呢?
没有更好只有更合适
那咱们就从无滚动条展开说说!!!
无滚动条设计
比如像element ui组件内像input的自定义模块数据过多的时候出现的下拉框内的滚动条,如下图:
在element-ui里面它其实是有内部组件el-scrollbar在的。那么它是怎么实现无滚动条呢?
如下图咱们先把:hover勾选上让滚动条一直处于显示得状态。然后咱们再分析他的实现。
当我把样式稍微修改下,咱们再观察下图:
这么看是不是就很明白了 他其实用margin值把整个容器扩大了点然后溢出隐藏,其实滚动条还在就是给界面上看不到了而已。
然后它自己用dom画了个滚动条,如下图:
经过上面分析,咱们已经很清楚得了解到一个无滚动条是从那个方面实现得了。
使用margin值把滚动条给溢出隐藏掉。使用div自己画了一个滚动条。方便咱们隐藏、显示、更改样式等。无滚动条实现
那咱们再从细节上拆分下具体实现要考虑那些点:
需要计算滚动条得宽度用来margin扩大得距离(每个界面上得滚动条得宽度是不一样得)。
需要计算画的div滚动条得高度(这个内容多少会影响滚动条的高度)。
需要根据滚动条去transform: translateY(19.3916%);移动咱们自己画的div滚动条的。
需要根据摁着画的div滚动条,去实际更改需要滚动的高度。
需要点击滚动轴的柱子支持跳到目标的滚动位置;一 计算界面原本滚动条的宽度
计算下界面上原本滚动条的宽度如下:
let scrollBarWidth;
export default function() {
if (scrollBarWidth !== undefined) return scrollBarWidth;
const outer = document.createElement('div');
outer.className = 'el-scrollbar__wrap';
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
return scrollBarWidth;
};
复制代码
先创建了一个div, 设置成scroll, 然后再在里面嵌套一个没有滚动条的div设置宽度100%, 获取到两者的offsetWidth, 相减获取到scrollBarWidth。赋值给scrollBarWidth 是惰性加载的优化,只需要计算一次就可以了。 具体展现如下图:
二 计算画的滚动条的高度height
计算下画的div滚动条的高度height。是用当前容器的内部高度 / 容器整个滚动条的高度 * 100计算出百分比;
比如:
const warp = this.$refs.wrap; // 或者使用documnet获取容器
const heightPercentage = (wrap.clientHeight * 100 / wrap.scrollHeight); // height
const widthPercentage = (wrap.clientWidth * 100 / wrap.scrollWidth); // width
复制代码
解析: 如当前容器高30px,内容撑起来总共高100px,那么滚动条的高度就是当前容器的30%;
三 计算滚动条需要移动的值
计算画的div需要滚动条的高度moveY是只有进程没有界面, 获取当前容器滚动的scrollTop / 当前容器内部高度 * 100:
算法一:
解析 使用transform: translateY(0%);是移动的是自己本身的百分比那么(容器滚动的scrollTop / 当前容器内部高度 * 100)算法如下:
const warp = this.$refs.wrap; // 或者使用documnet获取容器
this.moveY = ((wrap.scrollTop * 100) / wrap.clientHeight);
this.moveX = ((wrap.scrollLeft * 100) / wrap.clientWidth);
复制代码
算法二:
解析:使用定位top值,这个比较好理解滚动条的滚动 / 容器的滚动总高度 * 100得到百分比,如下:
const warp = this.$refs.wrap; // 或者使用documnet获取容器
this.moveY = ((wrap.scrollTop * 100) / wrap.scrollHeight);
this.moveX = ((wrap.scrollLeft * 100) / wrap.scrollWidth);
复制代码
把计算出来的moveY、moveX的方法 绑定给scroll 滚动事件就可以了。
四 摁着画的div滚动条 经行拖动
滚动条都是支持拖着上下左右移动的,那咱们也要支持下:
一个简单的拖动组件如下:
复制代码
咱们需要获取到画的滚动条的高度,然后根据拖动的距离算出来transform: translateY(0%);或者top值;
如上面拖动组件 拖动部分代码就不在重复了 咱们直接用diffX、diffY、lastX、lastY来用了。
算法一(transform)
const thumb = document.querySelector('el-scrollbar__thumb'); // element ui el-scrollbar 的滚动条
const { height: thumbHeight } = thumb?.getBoundingClientRect() || {};
const diffY = 10;
const lastY = '300'; // transform: translateY(300%);`
const moveY = (diffY / thumbHeight) + lastY;
复制代码
算法二(top)
const thumb = document.querySelector('el-scrollbar__thumb'); // element ui el-scrollbar 的滚动条
const { height: thumbHeight } = thumb?.getBoundingClientRect() || {};
const diffY = 10;
const lastY = 30; // top: 30%`
const moveY = (diffY / wrap.scrollWidth * 100) + lastY;
复制代码
五 点击滚动轴使滚动条跳转到该位置
function clickTrackHandler(event) {
const wrap = this.$refs.wrap;
// 1. 减去clientX 正好能获取到需要滚动到的位置
const offset = Math.abs(e.target.getBoundingClientRect().top - e.clientX);
// 2. 利用offset 的到画的滚动条的实际位置 两种算法transform || top
const thumb = document.querySelector('el-scrollbar__thumb'); // element ui el-scrollbar 的滚动条
const { height: thumbHeight } = thumb?.getBoundingClientRect() || {};
const translateY = offset / height * 100; // transform
const top = offset / wrap.scrollHeight * 100; // top
// 3、计算实际需要滚动的高度 使界面滚动到该位置。两种算法transform(scrollTop2) || top(scrollTop1)
const scrollTop1 = top * wrap.scrollHeight; // top
const scrollTop2 = translateY * wrap.clientHeight; // transform
}
复制代码
总结
针对无滚动条如果是vue使用的话,再了解具体实现后可以直接用element 的el-scrollbar组件就好,如果在其他框架中, 结合上面的逻辑也会很快封装一个组件。
版权声明
本文仅代表作者观点。
本文系作者授权发表,未经许可,不得转载。
发表评论