美文网首页
封装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