记录一下工作

需求

  导航菜单过长的时候会溢出,需要一个像 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>

 

 

 

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