美文网首页
封装swiper-view 选项卡导航条字体高亮,下标跟随

封装swiper-view 选项卡导航条字体高亮,下标跟随

作者: 叶叶叶xxx | 来源:发表于2019-06-05 18:03 被阅读0次
    使用了Vue-cli开发 封装

    先来看效果吧


    效果图
    swiper-view由两个部分组成

    使用vue-awesome-swiper

    //main.js
    import VueAwesomeSwiper from 'vue-awesome-swiper'
    import 'swiper/dist/css/swiper.css'
    import 'swiper/dist/js/swiper.min'
    

    1.header
    2.content


    swiper-view组件结构
    控制header参数

    参数 介绍 类型 可选值 默认值

    参数 介绍 类型 默认值
    iconClass 选项卡最右边icon String ' '
    iconColor 选项卡颜色 String '#fff'
    blurColor 未选中颜色 String 'rgb(126,159,175)'
    activeColor 选中的颜色 String 'rgb(255,255,255)'
    bgColor 背景颜色 String ''
    barwidth 下标宽度(px) Number 32
    tSpeed 下标释放移动速度(ms) Number 300
    slidesPerView 一排显示多少个 Number 3
    headerResistanceRatio 边缘抵抗力的大小比例(0-1) Number 0
    freeMode 自由滑动 Boolean true
    方法
    方法名 介绍 参数
    handleIconClick tabs最右边icon按钮 event
    handelItemClick 选项卡按钮icon按钮 event,index
    handelTabTouchStart 滑动开始时 event
    handelTabTouchMove 滑动中 event
    handelTabTouchEnd 滑动结束 event
    headerSwiper header初始化(不建议修改) tabsSwiper对象

    控制content参数
    参数 介绍 类型 默认值
    contentResistanceRatio 边缘抵抗力的大小比例 Number 0
    initialSlideIndex 刷新页面后swiper跳转到对应路由的index Number -1
    方法
    方法名 介绍 参数
    viewClick 点击事件 event
    handelViewTouchStart 滑动开始时 event
    handelViewTouchMove 滑动中 event
    handelViewTouchEnd 滑动结束时 event
    viewTransitionStart 回调函数,过渡开始时触发 没有

    父组件使用方式

    注意dataList的json结构...键一定要是title,component

    <template>
      <div class="father">
        <swiper-view :dataList="dataList"
                     ref="swiperView">
    
          <demo-a slot="demoA"></demo-a>
          <demo-b slot="demoB"></demo-b>
          <demo-c slot="demoC"></demo-c>
          
           <!--优雅写法,但是不好操作,直接显示的话可以用这个-->
    <!--
            <component :is="item.component"
                       :slot="item.component"
                       v-for="(item,id) of dataList"
                       :key="id">
            </component>
    -->
        </swiper-view>
      </div>
    </template>
    
    <script>
    import swiperView from 'common/components/swiperView/swiperView'
    name:"father"
    components: {
        swiperView,
        demoA: () => import('./otherComponents/demoA'),
        demoB: () => import('./otherComponents/demoB'),
        demoC: () => import('./otherComponents/demoC')
      },
    data () {
        return {
          dataList:
            [{ title: 'demo1',
              component: 'demoA'
            },
            { title: demo2',
              component: 'demoB'
            },
            { title: 'demo3',
              component: 'demoC'
            }]
        }    
      },
    </script>
    

    操作swiper

    在swiper-view中定义ref 写在计算属性中,就可以拿来使用了

    //父组件
    <template>
      <div class="mind-swiper-view-wrapper">
        <swiper-view ref="swiperView">
        </swiper-view>
      </div>
    </template>
    
    <script>
    //引入
    import swiperView from 'common/components/swiperView/swiperView'
     
    mounted () {
      console.log(this.viewEL.tabsSwiper)  //Swiper {…}
      console.log(this.viewEl.viewSwiper) //Swiper {…}
    },
    computed: {
      viewEL () {
        return this.$refs.swiperView
      }
    }
    </script>
    

    父组件修改样式

    使用 >>> 穿透的方式修改

    .mind-swiper-view-wrapper>>>.bar .color {
      background: red !important;
    }
    

    给header的item加上icon
    //父组件
    </template>
        <div class="iconfont iconClass"
             slot="demoA0">
        </div>
    </template>
    

    slot为component名字+index


    源码

    <template>
      <div class="swiperView-wrapper">
    
        <!-- header -->
        <div class="swiperView-header"
             ref="header">
          <!-- icon -->
          <div class="swiperView-header-icon"
               :style="{color:iconColor,background:bgColor}"
               v-if="iconClass !== ''"
               @click="handleIconClick">
            <div :class="['iconfont', iconClass]"></div>
          </div>
          <!-- swiper -->
          <swiper :class="['tabSwiper',iconClass !== '' ? 'hasIcon' : '']"
                  :style="{background:bgColor}"
                  :options="tabsOption"
                  ref="tabSwiper">
            <!-- slide -->
            <swiper-slide v-for="(item,index) of dataList"
                          :style="{color:blurColor}"
                          :key="index"
                          @click.native="handelItemClick($event,index)">
              <!-- item -->
              <div class="item">
                {{item.title}}
                <slot :name="item.component+index"></slot>
              </div>
            </swiper-slide>
            <div class="bar-box"
                 ref="bar">
              <div class="bar"></div>
            </div>
          </swiper>
        </div>
    
        <!-- content -->
        <div class="swiperView-content">
          <swiper ref="viewSwiper"
                  :options="viewOption">
            <swiper-slide v-for="(item,index) of dataList"
                          :key="index"
                          :data-index="index">
              <slot :name="item.component"></slot>
            </swiper-slide>
          </swiper>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'swiperView',
      props: {
        // ----------------header
        iconClass: {
          // 最右边的icon
          type: String,
          default: ''
        },
        iconColor: {
          // icon颜色
          type: String,
          default: '#fff'
        },
        blurColor: {
          // 未选中item颜色
          type: String,
          default: 'rgb(126,159,175)'
        },
        activeColor: {
          // 选中item颜色
          type: String,
          default: 'rgb(255,255,255)'
        },
        bgColor: {
          // 选项卡背景颜色
          type: String,
          default: ''
        },
        barwidth: {
          // 下标宽度
          type: Number,
          default: 32
        },
        tSpeed: {
          // 下标释放移动速度
          type: Number,
          default: 300
        },
        dataList: {
          // 数据
          type: Array,
          default: () => { return [] }
        },
        headerPerView: {
          // 头部显示多少个
          type: Number,
          default: 3
        },
        headerFreeMode: {
          // 自由滑动
          type: Boolean,
          default: false
        },
        headerResistanceRatio: {
          // 边缘抵抗力的大小比例
          type: Number,
          default: 0
        },
    
        // ----------------content
        contentResistanceRatio: {
          // 边缘抵抗力的大小比例
          type: Number,
          default: 0
        },
        initialSlideIndex: {
          // 刷新页面后swiper跳转到对应路由的index
          type: Number,
          default: -1
        }
      },
      data () {
        return {
          // tabsSwiper
          tabsOption: {
            slidesPerView: this.headerPerView,
            freeMode: this.headerFreeMode,
            resistanceRatio: this.headerResistanceRatio,
            on: {
              touchStart: this.handelTabTouchStart,
              touchMove: this.handelTabTouchMove,
              touchEnd: this.handelTabTouchEnd
            }
          },
          // viewSipwer
          viewOption: {
            watchSlidesProgress: true, // 计算每个slide的progress(进度、进程)
            resistanceRatio: this.contentResistanceRatio, // 边缘抵抗力的大小比例
            initialSlide: this.initialSlideIndex, // 刷新页面后swiper跳转到对应路由的index
            on: {
              // 点击事件
              tap: this.viewClick,
              // 滑动事件
              touchStart: this.handelViewTouchStart,
              touchMove: this.handelViewTouchMove,
              touchEnd: this.handelViewTouchEnd,
              // 回调函数,过渡开始时触发。
              transitionStart: this.viewTransitionStart
            }
          }
        }
      },
      computed: {
        tabsSwiper () { // tabsEl
          return this.$refs.tabSwiper.swiper
        },
        viewSwiper () { // viewEl
          return this.$refs.viewSwiper.swiper
        }
      },
      mounted () {
        // ----------------init
        this.$nextTick(() => {
          this.headerInit()
          this.contentInit()
          window.onresize = () => { // 页面重置
            this.headerInit()
          }
        })
      },
      methods: {
    
        // ----------------header
        headerInit () {
          // header初始化
          const TS = this.tabsSwiper // tabsSwiper对象
    
          const slides = TS.slides// 获取slides
    
          TS.len = slides.length // 优化性能
    
          TS.activeColor = [] // 选中颜色
    
          this.forRGB(this.activeColor, TS.activeColor)
    
          TS.blurColor = []// 未选中颜色
    
          this.forRGB(this.blurColor, TS.blurColor)
    
          if (TS.len && TS.len > 0) { // 递归 确定dom加载
            TS.navSum = slides[TS.len - 1].offsetLeft // 最后一个slide位置
    
            TS.header = this.$refs.header // 获取header
    
            TS.bar = this.$refs.bar // 导航下标
    
            TS.tSpeed = this.tSpeed // 初始化过渡速度
    
            TS.barwidth = this.barwidth // 初始化导航条的长度px
    
            TS.headerWidth = TS.header.clientWidth // 头部可视宽度
    
            TS.setTransition(TS.tSpeed) // 设置动画速度。.
    
            TS.slideWidth = parseInt(slides.eq(0).css('width'))// slide宽度
    
            TS.slideLeft = slides[this.$store.state.viewIndex].offsetLeft// 初始化slide距左边的距离
    
            TS.bar.style.width = TS.slideWidth // 设置下标对齐item
    
            TS.bar.style.transition = TS.tSpeed // 设置下标移动速度
    
            TS.bar.style.transform = 'translateX(' + TS.slideLeft + 'PX)' // bar跟随
    
            TS.slides.eq(TS.activeIndex).find('div').css('color', this.activeColor) // 设置点击中的item颜色
    
            this.$emit('headerSwiper', TS)
          }
        },
        handleIconClick (e) {
          // tabs最右边icon按钮
          this.$emit('handleIconClick', e)
        },
        handelItemClick (e, index) {
          // 选项卡按钮
          this.viewSwiper.slideTo(index, 0)
          this.$emit('handelItemClick', e, index)
        },
        handelTabTouchStart (e) {
          this.$emit('handelTabTouchStar', e)
        },
        handelTabTouchMove (e) {
          this.$emit('handelTabTouchMove', e)
        },
        handelTabTouchEnd (e) {
          this.$emit('handelTabTouchEnd', e)
        },
        // ----------------content
        contentInit () {
          this.viewSwiper.view = this.$refs.view // view DOM
    
          this.$emit('viewSwiper', this.viewSwiper.view)
        },
        // 点击事件
        viewClick (e) {
          this.$emit('viewClick', e)
        },
        // 滑动事件
        handelViewTouchStart (e) {
          // e.preventDefault()
          this.$emit('handelViewTouchStart', e)
        },
        handelViewTouchMove (e) {
          const VS = this.viewSwiper
          const TS = this.tabsSwiper
          this.progress = VS.progress// 监听偏移量
    
          let barPosition = TS.navSum * VS.progress // 下标位移坐标
          TS.bar.style.transition = 0
          TS.bar.style.transform = 'translateX(' + barPosition + 'px)'
    
          // --------颜色设置-------
          const aColor = TS.activeColor
          const bColor = TS.blurColor
          for (let i = 0; i < VS.slides.length; i++) {
            VS.slideProgress = VS.slides[i].progress
            if (Math.abs(VS.slideProgress) < 1) {
              TS.r = Math.floor(
                (aColor[0] - bColor[0]) * (1 - Math.pow(Math.abs(VS.slideProgress), 2)) + bColor[0]
              )
              TS.g = Math.floor(
                (aColor[1] - bColor[1]) * (1 - Math.pow(Math.abs(VS.slideProgress), 2)) + bColor[1]
              )
              TS.b = Math.floor(
                (aColor[2] - bColor[2]) * (1 - Math.pow(Math.abs(VS.slideProgress), 2)) + bColor[2]
              )
              TS.slides
                .eq(i)
                .find('div')
                .css('color', 'rgba(' + TS.r + ',' + TS.g + ',' + TS.b + ',1)')
            }
          }
          this.$emit('handelViewTouchMove', e)
        },
        handelViewTouchEnd (e) {
          this.$emit('handelViewTouchEnd', e)
        },
        // 回调函数,过渡开始时触发。
        viewTransitionStart () {
          const TS = this.tabsSwiper
          const VS = this.viewSwiper
          // 释放时导航粉色条移动过渡
          let index = VS.activeIndex
    
          TS.slidePositionLeft = TS.slides[index].offsetLeft
    
          TS.bar.style.transition = TS.tSpeed + 'ms'
    
          TS.bar.style.transform = 'translateX(' + TS.slidePositionLeft + 'PX)' // bar跟随
    
          // 释放时文字变色过渡
          TS.slides.eq(index).find('div').transition(TS.tSpeed)
    
          TS.slides.eq(index).find('div').css('color', this.activeColor)
    
          if (index > 0) {
            TS.slides.eq(index - 1).find('div').transition(TS.tSpeed)
    
            TS.slides.eq(index - 1).find('div').css('color', this.blurColor)
          }
    
          if (index < TS.len) {
            TS.slides.eq(index + 1).find('div').transition(TS.tSpeed)
    
            TS.slides.eq(index + 1).find('div').css('color', this.blurColor)
          }
    
          // 导航居中
          let maxTranslate = TS.maxTranslate() /* swiper最大偏移距离 */
    
          let maxWidth = -maxTranslate + TS.headerWidth / 2
    
          TS.slideLeft = TS.slides[index].offsetLeft // slide距左边的距离
    
          TS.slideCenter = TS.slideLeft + TS.slideWidth / 2 // 被点击slide的中心点距离
    
          TS.nowTranslate = TS.slideCenter - TS.headerWidth / 2
    
          TS.bar.transition = TS.tSpeed + 'ms'
    
          TS.$wrapperEl.transition(TS.tSpeed)
    
          TS.bar.style.transform = 'translateX(' + TS.slideLeft + 'PX)' // bar跟随
          if (TS.slideCenter < TS.headerWidth / 2) {
            TS.setTranslate(0)
          } else if (TS.slideCenter > maxWidth) {
            TS.setTranslate(maxTranslate)
          } else {
            TS.setTranslate(-TS.nowTranslate)
          }
          this.$emit('viewTransitionStart')
        },
        /** 字符串rgb()转提取数字转数组
        *  @param {string} color 需要转的颜色
        *  @param {Array} arr 存放数组的容器
        */
        forRGB (color, arr) {
          const rgbStr = color.replace(/[^0-9]/ig, ',')
          const strArr = rgbStr.split(',')
          strArr.forEach(num => {
            if (arr.length >= 3) return
            if (num) {
              arr.push(parseInt(num))
            }
          })
        }
      }
    }
    
    </script>
    
    <style lang="stylus" scoped>
    @import '~styles/varibles.styl';
    
    .swiperView-header {
      position: relative;
      z-index: 99;
      left: 0;
      top: 0;
      right: 0;
      width: 100%;
      height: 0;
      padding-bottom: $headerHeight;
      overflow: hidden;
      text-align: center;
      font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
    
      .hasIcon {
        margin-right: $headerHeight;
      }
    
      .tabSwiper {
        height: $headerHeight;
        line-height: $headerHeight;
    
        .item {
          width: 100%;
          height: 100%;
          transition: all 0.5s ease;
          font-size: 0.2rem;
          display: flex;
          justify-content: center;
          align-items: center;
    
          &:hover {
            color: #fff;
          }
        }
    
        .bar-box {
          position: absolute;
          bottom: 0.08rem;
          width: 1rem;
          height: 0.04rem;
    
          .bar {
            width: 0.64rem;
            height: 0.06rem;
            margin: 0 auto;
            background: #e0e0e0;
          }
        }
      }
    
      .swiperView-header-icon {
        position: absolute;
        right: 0;
        z-index: 99;
        width: $headerHeight;
        height: $headerHeight;
        font-size: 0.4rem;
    
        div {
          line-height: $headerHeight;
        }
      }
    }
    
    .swiperView-content {
      position: absolute;
      top: 0.8rem;
      left: 0;
      right: 0;
      bottom: 1.1rem;
    
      .component {
        width: 100%;
        height: 100%;
      }
    }
    </style>
    
    

    相关文章

      网友评论

          本文标题:封装swiper-view 选项卡导航条字体高亮,下标跟随

          本文链接:https://www.haomeiwen.com/subject/nibhxctx.html