You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
8.2 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view :class="`wd-swiper ${customClass}`" :style="customStyle">
<swiper
class="wd-swiper__track"
:autoplay="autoplay"
:current="navCurrent"
:interval="interval"
:duration="duration"
:circular="loop"
:vertical="direction == 'vertical'"
:easing-function="easingFunction"
:previous-margin="previousMargin"
:next-margin="nextMargin"
:snap-to-edge="snapToEdge"
:display-multiple-items="displayMultipleItems"
:style="{ height: addUnit(height) }"
@change="handleChange"
@animationfinish="handleAnimationfinish"
>
<swiper-item v-for="(item, index) in list" :key="index" class="wd-swiper__item" @click="handleClick(index)">
<image
:src="isObj(item) ? item.value : item"
:class="`wd-swiper__image ${customImageClass} ${getCustomImageClass(navCurrent, index, list)}`"
:style="{ height: addUnit(height) }"
:mode="imageMode"
/>
</swiper-item>
</swiper>
<template v-if="indicator">
<slot name="indicator" :current="navCurrent" :total="list.length"></slot>
<wd-swiper-nav
v-if="!$slots.indicator"
:custom-class="customIndicatorClass"
:type="swiperIndicator.type"
:current="swiperIndicator.current"
:total="swiperIndicator.total"
:direction="swiperIndicator.direction"
:indicator-position="swiperIndicator.indicatorPosition"
:min-show-num="swiperIndicator.minShowNum"
:show-controls="swiperIndicator.showControls"
@change="handleIndicatorChange"
/>
</template>
</view>
</template>
<script lang="ts" setup>
import { computed, onBeforeMount, ref } from 'vue'
import { addUnit, isObj } from '../common/util'
import type { DirectionType, EasingType, IndicatorPositionType, SwiperList } from './type'
import type { SwiperIndicatorType } from '../wd-swiper-nav/type'
interface SwiperIndicatorProps {
/**
* 当前轮播在哪一项(下标)
* @default 0
*/
current?: number
/**
* 轮播滑动方向,包括横向滑动和纵向滑动两个方向
* @default horizontal
*/
direction?: DirectionType
/**
* 小于这个数字不会显示导航器
* @default 2
*/
minShowNum?: number
/**
* 页码信息展示位置
* @default bottom
*/
indicatorPosition?: IndicatorPositionType
/**
* 是否显示两侧的控制按钮
* @default false
*/
showControls?: boolean
/**
* 总共的项数
* @default 0
*/
total?: number
/**
* 导航器类型,点状(dots)、点条状(dots-bar)、分式(fraction)等
* @default dots
*/
type?: SwiperIndicatorType
}
interface Props {
/**
* 是否自动播放
* @default true
*/
autoplay?: boolean
/**
* 当前轮播在哪一项(下标)
* @default 0
*/
current?: number
/**
* 轮播滑动方向,包括横向滑动和纵向滑动两个方向
* @default horizontal
*/
direction?: DirectionType
/**
* 同时显示的滑块数量
* @default 1
*/
displayMultipleItems?: number
/**
* 滑动动画时长
* @default 300
*/
duration?: number
/**
* 指定 swiper 切换缓动动画类型
* @default default
*/
easingFunction?: EasingType
/**
* 轮播的高度;默认单位 `px`
* @default 192
*/
height?: string | number
/**
* 轮播间隔时间
* @default 5000
*/
interval?: number
/**
* 图片列表
*/
list?: string[] | SwiperList[]
/**
* 是否循环播放
* @default true
*/
loop?: boolean
/**
* 后边距,可用于露出后一项的一小部分。默认单位 `px`
* @default 0
*/
nextMargin?: string | number
/**
* 页码信息展示位置
* @default bottom
*/
indicatorPosition?: IndicatorPositionType
/**
* 前边距,可用于露出前一项的一小部分。默认单位 `px`
* @default 0
*/
previousMargin?: string | number
/**
* 当 swiper-item 的个数大于等于 2关闭 circular 并且开启 previous-margin 或 next-margin 的时候,可以指定这个边距是否应用到第一个、最后一个元素
* @default false
*/
snapToEdge?: boolean
/**
* 指示器全部配置true 的话使用默认配置
* @default true
*/
indicator?: SwiperIndicatorProps | boolean
/**
* 图片裁剪、缩放的模式
*/
imageMode?: number | string
/**
* 外部自定义样式
*/
customStyle?: string
/**
* 外部自定义类名
*/
customClass?: string
/**
* 自定义指示器类名
*/
customIndicatorClass?: string
/**
* 自定义图片类名
*/
customImageClass?: string
/**
* 自定义上一个图片类名
*/
customPrevImageClass?: string
/**
* 自定义下一个图片类名
*/
customNextImageClass?: string
}
const props = withDefaults(defineProps<Props>(), {
customStyle: '',
customClass: '',
customIndicatorClass: '',
customImageClass: '',
customPrevImageClass: '',
customNextImageClass: '',
autoplay: true,
current: 0,
direction: 'horizontal',
displayMultipleItems: 1,
duration: 300,
easingFunction: 'default',
height: '192',
interval: 5000,
list: () => [],
loop: true,
indicator: true,
nextMargin: '0',
indicatorPosition: 'bottom',
previousMargin: '0',
snapToEdge: false,
imageMode: 'aspectFill'
})
const navCurrent = ref<number>(0) // 当前滑块
onBeforeMount(() => {
navCurrent.value = props.current
})
const swiperIndicator = computed(() => {
const { list, direction, indicatorPosition, indicator } = props
const swiperIndicator: SwiperIndicatorProps = {
current: navCurrent.value || 0,
total: list.length || 0,
direction: direction || 'horizontal',
indicatorPosition: indicatorPosition || 'bottom'
}
if (isObj(indicator)) {
swiperIndicator.type = indicator.type || 'dots'
swiperIndicator.minShowNum = indicator.minShowNum || 2
swiperIndicator.showControls = indicator.showControls || false
}
return swiperIndicator
})
const emit = defineEmits(['click', 'change', 'animationfinish'])
/**
* 是否为当前滑块的前一个滑块
* @param current
* @param index
* @param list
*/
function isPrev(current: number, index: number, list: string[] | SwiperList[]) {
return (current - 1 + list.length) % list.length === index
}
/**
* 是否为当前滑块的后一个滑块
* @param current
* @param index
* @param list
*/
function isNext(current: number, index: number, list: string[] | SwiperList[]) {
return (current + 1 + list.length) % list.length === index
}
function getCustomImageClass(current: number, index: number, list: string[] | SwiperList[]) {
let customImageClass: string = ''
if (isPrev(current, index, list)) {
customImageClass = props.customPrevImageClass
}
if (isNext(current, index, list)) {
customImageClass = props.customNextImageClass
}
return customImageClass
}
/**
* 轮播滑块切换时触发
* @param e
*/
function handleChange(e: { detail: { current: any; source: string } }) {
const { current, source } = e.detail
navCurrent.value = current
emit('change', { current, source })
}
/**
* 滑块动画结束
*/
function handleAnimationfinish(e: { detail: { current: any; source: string } }) {
const { current, source } = e.detail
// navCurrent.value = current
/**
* 滑块动画结束时触发
* @arg value:Object 滑块值
*/
emit('animationfinish', { current, source })
}
/**
* 点击滑块事件
* @param index 点击的滑块下标
*/
function handleClick(index: number) {
emit('click', { index })
}
function handleIndicatorChange(e: { dir: any; source: string }) {
const { dir, source } = e
doIndicatorBtnChange(dir, source)
}
function doIndicatorBtnChange(dir: string, source: string) {
const { list, loop } = props
const total = list.length
let nextPos = dir === 'next' ? navCurrent.value + 1 : navCurrent.value - 1
if (loop) {
nextPos = dir === 'next' ? (navCurrent.value + 1) % total : (navCurrent.value - 1 + total) % total
} else {
nextPos = nextPos < 0 || nextPos >= total ? navCurrent.value : nextPos
}
if (nextPos === navCurrent.value) return
navCurrent.value = nextPos
emit('change', { current: nextPos, source })
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>