最近闲来无事,对平常用的饿了么UI框架感兴趣了,很好奇element-ui对tabs标签页下面的滑动条滑动是如何实现的。说实话,我就喜欢研究与浏览器交互的东西,哈哈哈。
1.tabs标签页
通过阅读源码,知道标签页下方的滑动条是单独写的一个组件叫做tab-bar
组件。这个组件只接受tabs属性。当这个tabs属性值变化的时候,又通过计算属性barStyle
来计算出滑动条需要滑行(translate
)的距离和其本身的宽度。核心源码见下方。
2.核心代码
computed: {
barStyle: {
get() {
let style = {};
let offset = 0;
let tabSize = 0;
const sizeName = ['top', 'bottom'].indexOf(this.rootTabs.tabPosition) !== -1 ? 'width' : 'height';
const sizeDir = sizeName === 'width' ? 'x' : 'y';
const firstUpperCase = str => {
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
};
// every方法遇到当自定义回调函数返回false时,停止遍历。否则,遍历全部数组元素
// 这里是滑动条滑动距离计算的核心代码
this.tabs.every((tab, index) => {
let $el = arrayFind(this.$parent.$refs.tabs || [], t => t.id.replace('tab-', '') === tab.paneName);
if (!$el) { return false; }
// active属性为false时,累加其元素宽度
if (!tab.active) {
offset += $el[`client${firstUpperCase(sizeName)}`];
return true;
} else {
// active属性为true时,加上该元素的左边距就是滑动条需要滑行的距离了。
tabSize = $el[`client${firstUpperCase(sizeName)}`];
const tabStyles = window.getComputedStyle($el);
if (sizeName === 'width' && this.tabs.length > 1) {
// 滑动条的宽度是标签页去掉其左右内边距
tabSize -= parseFloat(tabStyles.paddingLeft) + parseFloat(tabStyles.paddingRight);
}
if (sizeName === 'width') {
offset += parseFloat(tabStyles.paddingLeft);
}
return false;
}
});
const transform = `translate${firstUpperCase(sizeDir)}(${offset}px)`;
style[sizeName] = tabSize + 'px';
style.transform = transform;
style.msTransform = transform;
style.webkitTransform = transform;
return style;
}
}
}
3.如何实现滑动条滑动的呢
它是通过遍历所有的标签组件,当组件的active属性为false时,表明它未被点击选中,通过window.getComputedStyle($el)
获取它的DOM元素的宽度,进行累加。一直遍历到active属性为true时,停止遍历,累加的宽度的和,就是滑动条要滑动的距离。
4.收获
- 数组
every
方法遇到当自定义回调函数返回false时,停止遍历。否则,遍历全部数组元素。 window.getComputedStyle($el)
获取当前元素所有最终使用的CSS属性值
。返回的是一个CSS样式声明对象(object CSSStyleDeclaration),只读。也就是说,获取到的不仅仅是我们自定义的样式,它包含了所有作用在当前元素上的css属性及属性值。
(小知识点:window.getComputedStyle()
获取的样式值,必须是声明过的。而Element.getBoundingClientRect()
这个方法获取的值,是浏览器计算的,而且,是number类型的,不带px单位)