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

 

  由于时间问题,所以直接将 Tabs 源码抽取出来使用。

  这里要做一些特殊处理,不允许 NavMenu 导航菜单滚动视图容器内的元素换行。

如: 

/deep/.el-menu–horizontal>.el-menu-item,.el-menu–horizontal>.el-submenu {
    float: none !important;
    display: inline-block !important;
  }

使用方法如下:

  1. <scrollView>
  2. <div>
  3. // 一些不换行内容
  4. </div>
  5. </scrollView>

 

下面是抽出来的源码,稍稍改造了下,左右箭头做了自适应垂直居中,把 elementui的样式抽取出来以及修改了类名,减少依赖。

scrollView.vue 

  1. <script>
  2. import {
  3. addResizeListener,
  4. removeResizeListener
  5. } from "element-ui/src/utils/resize-event";
  6. const firstUpperCase = str => {
  7. return str.toLowerCase().replace(/( |^)[a-z]/g, L => L.toUpperCase());
  8. };
  9. export default {
  10. props: {},
  11. data () {
  12. return {
  13. scrollable: false,
  14. navOffset: 0,
  15. isFocus: false,
  16. focusable: true,
  17. tabPosition: "top"
  18. };
  19. },
  20. computed: {
  21. navStyle () {
  22. const dir =
  23. ["top", "bottom"].indexOf(this.tabPosition) !== -1 ? "X" : "Y";
  24. return {
  25. transform: `translate${dir}(-${this.navOffset}px)`
  26. };
  27. },
  28. sizeName () {
  29. return ["top", "bottom"].indexOf(this.tabPosition) !== -1
  30. ? "width"
  31. : "height";
  32. }
  33. },
  34. methods: {
  35. scrollPrev () {
  36. const containerSize = this.$refs.navScroll[
  37. `offset${firstUpperCase(this.sizeName)}`
  38. ];
  39. const currentOffset = this.navOffset;
  40. if (!currentOffset) return;
  41. let newOffset =
  42. currentOffset > containerSize ? currentOffset - containerSize : 0;
  43. this.navOffset = newOffset;
  44. },
  45. scrollNext () {
  46. const navSize = this.$refs.nav[`offset${firstUpperCase(this.sizeName)}`];
  47. const containerSize = this.$refs.navScroll[
  48. `offset${firstUpperCase(this.sizeName)}`
  49. ];
  50. const currentOffset = this.navOffset;
  51. if (navSize - currentOffset <= containerSize) return;
  52. let newOffset =
  53. navSize - currentOffset > containerSize * 2
  54. ? currentOffset + containerSize
  55. : navSize - containerSize;
  56. this.navOffset = newOffset;
  57. },
  58. scrollToActiveTab () {
  59. if (!this.scrollable) return;
  60. const nav = this.$refs.nav;
  61. const activeTab = this.$el.querySelector(".is-active");
  62. if (!activeTab) return;
  63. const navScroll = this.$refs.navScroll;
  64. const isHorizontal = ["top", "bottom"].indexOf(this.tabPosition) !== -1;
  65. const activeTabBounding = activeTab.getBoundingClientRect();
  66. const navScrollBounding = navScroll.getBoundingClientRect();
  67. const maxOffset = isHorizontal
  68. ? nav.offsetWidth - navScrollBounding.width
  69. : nav.offsetHeight - navScrollBounding.height;
  70. const currentOffset = this.navOffset;
  71. let newOffset = currentOffset;
  72. if (isHorizontal) {
  73. if (activeTabBounding.left < navScrollBounding.left) {
  74. newOffset =
  75. currentOffset - (navScrollBounding.left - activeTabBounding.left);
  76. }
  77. if (activeTabBounding.right > navScrollBounding.right) {
  78. newOffset =
  79. currentOffset + activeTabBounding.right - navScrollBounding.right;
  80. }
  81. } else {
  82. if (activeTabBounding.top < navScrollBounding.top) {
  83. newOffset =
  84. currentOffset - (navScrollBounding.top - activeTabBounding.top);
  85. }
  86. if (activeTabBounding.bottom > navScrollBounding.bottom) {
  87. newOffset =
  88. currentOffset +
  89. (activeTabBounding.bottom - navScrollBounding.bottom);
  90. }
  91. }
  92. newOffset = Math.max(newOffset, 0);
  93. this.navOffset = Math.min(newOffset, maxOffset);
  94. },
  95. update () {
  96. if (!this.$refs.nav) return;
  97. const sizeName = this.sizeName;
  98. const navSize = this.$refs.nav[`offset${firstUpperCase(sizeName)}`];
  99. this.height = this.$refs.nav[`offset${firstUpperCase('height')}`];
  100. const containerSize = this.$refs.navScroll[
  101. `offset${firstUpperCase(sizeName)}`
  102. ];
  103. const currentOffset = this.navOffset;
  104. if (containerSize < navSize) {
  105. const currentOffset = this.navOffset;
  106. this.scrollable = this.scrollable || {};
  107. this.scrollable.prev = currentOffset;
  108. this.scrollable.next = currentOffset + containerSize < navSize;
  109. if (navSize - currentOffset < containerSize) {
  110. this.navOffset = navSize - containerSize;
  111. }
  112. } else {
  113. this.scrollable = false;
  114. if (currentOffset > 0) {
  115. this.navOffset = 0;
  116. }
  117. }
  118. }
  119. },
  120. updated () {
  121. this.update();
  122. },
  123. render () {
  124. const { navStyle, scrollable, scrollNext, scrollPrev, height } = this;
  125. const lineHeight = {
  126. 'line-height' : height + 'px'
  127. }
  128. const scrollBtn = scrollable
  129. ? [
  130. <span
  131. class={["scrollView__nav-prev", scrollable.prev ? "" : "is-disabled"]}
  132. on-click={scrollPrev}
  133. >
  134. <i
  135. style={lineHeight}
  136. class="el-icon-arrow-left"></i>
  137. </span>,
  138. <span
  139. class={["scrollView__nav-next", scrollable.next ? "" : "is-disabled"]}
  140. on-click={scrollNext}
  141. >
  142. <i style={lineHeight}
  143. class="el-icon-arrow-right"></i>
  144. </span>
  145. ]
  146. : null;
  147. return (
  148. <div
  149. class={[
  150. "scrollView__nav-wrap",
  151. scrollable ? "is-scrollable" : "",
  152. `is-${this.tabPosition}`
  153. ]}
  154. style={{ width: "100%" }}
  155. >
  156. {scrollBtn}
  157. <div
  158. class={["scrollView__nav-scroll"]}
  159. ref="navScroll"
  160. >
  161. <div
  162. class={["scrollView__nav", `is-${this.tabPosition}`]}
  163. ref="nav"
  164. style={navStyle}
  165. role="tablist"
  166. >
  167. {this.$slots.default}
  168. </div>
  169. </div>
  170. </div>
  171. );
  172. },
  173. mounted () {
  174. addResizeListener(this.$el, this.update);
  175. },
  176. beforeDestroy () {
  177. if (this.$el && this.update) removeResizeListener(this.$el, this.update);
  178. }
  179. };
  180. </script>
  181.  
  182. <style lang="less">
  183. .scrollView__nav-wrap {
  184. overflow: hidden;
  185. margin-bottom: -1px;
  186. position: relative;
  187. }
  188. .scrollView__nav-wrap.is-scrollable {
  189. padding: 0 20px;
  190. -webkit-box-sizing: border-box;
  191. box-sizing: border-box;
  192. }
  193. .scrollView__nav-wrap::after {
  194. display: none;
  195. }
  196. .scrollView__nav-scroll {
  197. overflow: hidden;
  198. }
  199. .scrollView__nav {
  200. white-space: nowrap;
  201. position: relative;
  202. transition: transform 0.3s, -webkit-transform 0.3s;
  203. float: left;
  204. z-index: 2;
  205. }
  206. .scrollView__nav-prev {
  207. left: 0;
  208. }
  209. .scrollView__nav-next {
  210. right: 0;
  211. }
  212. .scrollView__nav-next, .scrollView__nav-prev {
  213. position: absolute;
  214. cursor: pointer;
  215. line-height: 44px;
  216. font-size: 12px;
  217. color: #909399;
  218. }
  219. </style>

 

 

 

版权声明:本文为penglianger原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/penglianger/p/12489234.html