使用了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>
网友评论