使用 better-scroll 实现纵向横向双向滚动并设置吸顶

使用 better-scroll 实现纵向横向双向滚动

需要达到的效果:右侧内容左右滚动时,左侧边栏不动,上下滑动时整体内容上下滚动

如图:
使用 better-scroll 实现纵向横向双向滚动并设置吸顶_第1张图片
1.纵向滚动

安装并引入

import BScroll from 'better-scroll'

因为滚动默认是纵向的,所以不需要多设置其他参数

首先在 HTML 部分设置滚动外层容器(centent)和滚动内容(content-box),设置外层 div 的高度比如 100vh,content-box 高度由内容撑开。打开控制台查看是否 content-box 高度大于 content 高度,如果是,基本就没问题。

<div ref="wrapper" class="content">
  <div class="content-box">
    <ul>
      ...
    ul>
  div>
div>
let scroll = new BScroll(this.$refs.wrapper)

2.横向滚动

在纵向滚动的基础上添加横向滚动,需要注意的是样式的设定。为了让右边内容左右滚动的时候左边侧栏能够固定,所以我的办法是左边侧栏和右边滚动内容分开来写。

左边侧栏宽度设为 90px,右边内容宽度动态获取。right-detail 里的 ul 宽度要比 right-detail 的宽度值大。要使右边 ul 里的内容横向排列(比如 li)可以使用 white-space: nowrap; 同时还要注意其他样式的设定。具体样式看文章末尾贴的代码。

<div ref="wrapper" :style="{ width: originWidth + 'px' }" class="content">
  <div>
    <p class="title">
      界面标题
    p>
    <div class="box-content">
      
      <div class="left-name">
        <ul>
          <li>
            <span>固定的左边标题span>
          li>

          <li v-for="(item, index) in 数据" :key="index">
            <span>{{ 动态左边侧栏}}span>
          li>
        ul>
      div>
      
      <div class="right-detail" ref="listWrapper" :style="{ width: originWidth - 90 + 'px' }">
        <ul>
          <li>
            ...固定的右边标题或者动态获取的
          li>
          <li v-for="(item, index) in 数据" :key="index">
            ...动态的右边内容区域
          li>
        ul>
      div>
    div>
  div>
div>
data() {
    return {
      originWidth: '',
    }
  },
 mounted() {
    this.$nextTick(() => {
      this.listScroll()
    })
  },
  methods: {
    listScroll() {
      let originWidth = document.getElementsByClassName('page')[0].clientWidth
      this.originWidth = originWidth

      let scroll = new BScroll(this.$refs.wrapper)

      let horizontalScroll = new BScroll(this.$refs.listWrapper, {
        scrollX: true,
        eventPassthrough: 'vertical',
      })
    },
  }

需要注意这里 eventPassthrough 的值是’vertical’,而不是 ‘horizontal’。freeScroll、scrollX、eventPassthrough(horizontal)都是支持横向滚动,但是彼此之间会互斥。

以上就实现了纵向横向的滚动效果。

3.添加效果:横向滚动左边侧栏右侧显示阴影,纵向滚动列表表头吸顶
因为左边侧栏和右边内容区域是分开写的,上下滚动需要吸顶,左右滚动侧栏不动,加上 scroll 组件包裹的容器使用的样式是 transform 导致 fixed 失效。所以我的做法是在滚动层最外面另外做一个吸顶 ”狸猫换太子“。监听滚动距离,显示隐藏吸顶和样式。


<div v-if="fixedShow" :class="topFixShadow" class="top-fix">
  <div class="left-name">
    <ul :class="boxShadow">
      <li class="fix-name">
        <span>固定的左边标题span>
      li>
    ul>
  div>
  <div :style="{ 'margin-left': left + 'px' }" class="right-detail top-right-detail">
    <ul>
      <li>
        ...固定的右边标题或者动态获取的
      li>
    ul>
  div>
div>

<div ref="wrapper" :style="{ width: originWidth + 'px' }" class="content">
  <div>
    <p class="title">
      界面标题
    p>
    <div class="box-content">
      <div class="left-name">
        <ul :class="boxShadow">
          <li :class="showTitle">
            <span>固定的左边标题span>
          li>
          <li v-for="(item, index) in 数据" :key="index">
            <span>{{ 动态左边侧栏 }}span>
          li>
        ul>
      div>
      
      <div ref="listWrapper" :style="{ width: originWidth - 90 + 'px' }" class="right-detail">
        <ul>
          <li :class="showTitle">
            ...固定的右边标题或者动态获取的
          li>
          <li v-for="(item, index) in 数据" :key="index">
            ...动态的右边内容区域
          li>
        ul>
      div>
    div>
  div>
div>
  data() {
    return {
      originWidth: '',
      boxShadow: '',
      fixName: '',
      left: '',
      fixedShow: false,
      topFixShadow: '',
      showTitle: '',
    }
  },
 mounted() {
    this.$nextTick(() => {
      this.listScroll()
    })
  },
  methods: {
    listScroll() {
      let originWidth = document.getElementsByClassName('page')[0].clientWidth
      this.originWidth = originWidth

      let scroll = new BScroll(this.$refs.wrapper, {
        click: true,
        probeType: 3,
      })
      scroll.on('scroll', ({ x, y }) => {
        if (y < -34) {     // 纵向滚动到一定距离时,吸顶显示,原设吸顶display:none。
          this.fixedShow = true
          this.showTitle = 'show-title'
          this.topFixShadow = 'top-fix-shadow'
        } else {
          this.fixedShow = false
          this.showTitle = ''
          this.topFixShadow = ''
        }
      })

      let horizontalScroll = new BScroll(this.$refs.listWrapper, {
        scrollX: true,
        click: true,
        probeType: 3,
        eventPassthrough: 'vertical',
      })

      horizontalScroll.on('scroll', ({ x, y }) => {
        if (x < 0) {      // 当横向滚动时,设置左边侧栏阴影,并使外部的吸顶随着滚动距离移动。
          this.boxShadow = 'box-shadow'
          this.left = x
        } else {
          this.boxShadow = ''
        }
      })

    },
  }

需要注意的是 probeType 为 1 的时候,会非实时(屏幕滑动超过一定时间后)派发 scroll 事件;当 probeType 为 2 的时候,会在屏幕滑动的过程中实时的派发 scroll 事件;当 probeType 为 3 的时候,不仅在屏幕滑动的过程中,而且在 momentum 滚动动画运行过程中实时派发 scroll 事件。如果没有设置该值,其默认值为 0,即不派发 scroll 事件。

全部样式:

.page {
  height: 100%;
  display: flex;
  flex-direction: column;
  .content {
    flex: 1;
    overflow: hidden;
    position: relative;
    .title {
      padding: 9px 12px;
    }
  }
}
.top-fix {
  display: flex;
  position: relative;
  z-index: 9999;
}
.top-right-detail {
  flex: 1;
}
.top-fix-shadow {
  box-shadow: 3px 3px 3px #ddd;
}
.show-title {
  display: none;
}
.box-content {
  flex: 1;
  border-top: 0.5px solid #ddd;
  border-bottom: 1px solid #ddd;
  overflow: scroll;
  z-index: 999;
  zoom: 1;
}
.left-name {
  width: 90px;
  float: left;
  position: relative;
  z-index: 9999;
  li {
    text-align: center;
    &:not(:last-child) {
      border-bottom: 1px solid #ddd;
    }
  }
  li:nth-child(even) {
    background-color: #f4f4f4;
  }
  span {
    line-height: 33px;
    font-size: 14px;
    color: #777777;
  }
}
.scroll-content {
  width: 100%;
}
.right-detail {
  float: left;
  height: auto;
  width: auto;

  overflow: hidden;
  ul {
    white-space: nowrap;
    float: left;
    li {
      span {
        width: 70px;
        display: inline-block;
        text-align: center;
        line-height: 33px;
        font-size: 14px;
        color: #777777;
      }
      &:not(:last-child) {
        border-bottom: 1px solid #ddd;
      }
    }
    li:nth-child(even) {
      background-color: #f4f4f4;
    }
  }
}

.box-content:after {
  content: '';
  display: block;
  visibility: hidden;
  clear: both;
  height: 0;
}
.fix-name {
  width: 90px;
  height: 33px;
  background-color: #f9f9f9;
}
.box-shadow {
  box-shadow: 2px 0px 2px #ddd;
}

关注公众号bug人生回复资料即可获得前端视频资料

你可能感兴趣的:(vue)