记录一下工作
需求
导航菜单过长的时候会溢出,需要一个像 Tabs 标签页一样的滚动视图容器,可以左右滚动内部视图。

解决方法
由于时间问题,所以直接将 Tabs 源码抽取出来使用。
这里要做一些特殊处理,不允许 NavMenu 导航菜单滚动视图容器内的元素换行。
如:
/deep/.el-menu--horizontal>.el-menu-item,.el-menu--horizontal>.el-submenu {
float: none !important;
display: inline-block !important;
}
使用方法如下:
<scrollView>
<div>
// 一些不换行内容
</div>
</scrollView>
下面是抽出来的源码,稍稍改造了下,左右箭头做了自适应垂直居中,把 elementui的样式抽取出来以及修改了类名,减少依赖。
scrollView.vue
<script>
import {
addResizeListener,
removeResizeListener
} from "element-ui/src/utils/resize-event";
const firstUpperCase = str => {
return str.toLowerCase().replace(/( |^)[a-z]/g, L => L.toUpperCase());
};
export default {
props: {},
data () {
return {
scrollable: false,
navOffset: 0,
isFocus: false,
focusable: true,
tabPosition: "top"
};
},
computed: {
navStyle () {
const dir =
["top", "bottom"].indexOf(this.tabPosition) !== -1 ? "X" : "Y";
return {
transform: `translate${dir}(-${this.navOffset}px)`
};
},
sizeName () {
return ["top", "bottom"].indexOf(this.tabPosition) !== -1
? "width"
: "height";
}
},
methods: {
scrollPrev () {
const containerSize = this.$refs.navScroll[
`offset${firstUpperCase(this.sizeName)}`
];
const currentOffset = this.navOffset;
if (!currentOffset) return;
let newOffset =
currentOffset > containerSize ? currentOffset - containerSize : 0;
this.navOffset = newOffset;
},
scrollNext () {
const navSize = this.$refs.nav[`offset${firstUpperCase(this.sizeName)}`];
const containerSize = this.$refs.navScroll[
`offset${firstUpperCase(this.sizeName)}`
];
const currentOffset = this.navOffset;
if (navSize - currentOffset <= containerSize) return;
let newOffset =
navSize - currentOffset > containerSize * 2
? currentOffset + containerSize
: navSize - containerSize;
this.navOffset = newOffset;
},
scrollToActiveTab () {
if (!this.scrollable) return;
const nav = this.$refs.nav;
const activeTab = this.$el.querySelector(".is-active");
if (!activeTab) return;
const navScroll = this.$refs.navScroll;
const isHorizontal = ["top", "bottom"].indexOf(this.tabPosition) !== -1;
const activeTabBounding = activeTab.getBoundingClientRect();
const navScrollBounding = navScroll.getBoundingClientRect();
const maxOffset = isHorizontal
? nav.offsetWidth - navScrollBounding.width
: nav.offsetHeight - navScrollBounding.height;
const currentOffset = this.navOffset;
let newOffset = currentOffset;
if (isHorizontal) {
if (activeTabBounding.left < navScrollBounding.left) {
newOffset =
currentOffset - (navScrollBounding.left - activeTabBounding.left);
}
if (activeTabBounding.right > navScrollBounding.right) {
newOffset =
currentOffset + activeTabBounding.right - navScrollBounding.right;
}
} else {
if (activeTabBounding.top < navScrollBounding.top) {
newOffset =
currentOffset - (navScrollBounding.top - activeTabBounding.top);
}
if (activeTabBounding.bottom > navScrollBounding.bottom) {
newOffset =
currentOffset +
(activeTabBounding.bottom - navScrollBounding.bottom);
}
}
newOffset = Math.max(newOffset, 0);
this.navOffset = Math.min(newOffset, maxOffset);
},
update () {
if (!this.$refs.nav) return;
const sizeName = this.sizeName;
const navSize = this.$refs.nav[`offset${firstUpperCase(sizeName)}`];
this.height = this.$refs.nav[`offset${firstUpperCase('height')}`];
const containerSize = this.$refs.navScroll[
`offset${firstUpperCase(sizeName)}`
];
const currentOffset = this.navOffset;
if (containerSize < navSize) {
const currentOffset = this.navOffset;
this.scrollable = this.scrollable || {};
this.scrollable.prev = currentOffset;
this.scrollable.next = currentOffset + containerSize < navSize;
if (navSize - currentOffset < containerSize) {
this.navOffset = navSize - containerSize;
}
} else {
this.scrollable = false;
if (currentOffset > 0) {
this.navOffset = 0;
}
}
}
},
updated () {
this.update();
},
render () {
const { navStyle, scrollable, scrollNext, scrollPrev, height } = this;
const lineHeight = {
'line-height' : height + 'px'
}
const scrollBtn = scrollable
? [
<span
class={["scrollView__nav-prev", scrollable.prev ? "" : "is-disabled"]}
on-click={scrollPrev}
>
<i
style={lineHeight}
class="el-icon-arrow-left"></i>
</span>,
<span
class={["scrollView__nav-next", scrollable.next ? "" : "is-disabled"]}
on-click={scrollNext}
>
<i style={lineHeight}
class="el-icon-arrow-right"></i>
</span>
]
: null;
return (
<div
class={[
"scrollView__nav-wrap",
scrollable ? "is-scrollable" : "",
`is-${this.tabPosition}`
]}
style={{ width: "100%" }}
>
{scrollBtn}
<div
class={["scrollView__nav-scroll"]}
ref="navScroll"
>
<div
class={["scrollView__nav", `is-${this.tabPosition}`]}
ref="nav"
style={navStyle}
role="tablist"
>
{this.$slots.default}
</div>
</div>
</div>
);
},
mounted () {
addResizeListener(this.$el, this.update);
},
beforeDestroy () {
if (this.$el && this.update) removeResizeListener(this.$el, this.update);
}
};
</script>
<style lang="less">
.scrollView__nav-wrap {
overflow: hidden;
margin-bottom: -1px;
position: relative;
}
.scrollView__nav-wrap.is-scrollable {
padding: 0 20px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.scrollView__nav-wrap::after {
display: none;
}
.scrollView__nav-scroll {
overflow: hidden;
}
.scrollView__nav {
white-space: nowrap;
position: relative;
transition: transform 0.3s, -webkit-transform 0.3s;
float: left;
z-index: 2;
}
.scrollView__nav-prev {
left: 0;
}
.scrollView__nav-next {
right: 0;
}
.scrollView__nav-next, .scrollView__nav-prev {
position: absolute;
cursor: pointer;
line-height: 44px;
font-size: 12px;
color: #909399;
}
</style>
来源:https://www.cnblogs.com/penglianger/p/12489234.html