CNCEC_APP/uni_modules/uview-pro/components/u-swiper/u-swiper.vue

309 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view
class="u-swiper-wrap"
:style="$u.toStyle({ borderRadius: `${borderRadius}rpx` }, customStyle)"
:class="customClass"
>
<swiper
:current="elCurrent"
@change="change"
@animationfinish="animationfinish"
:interval="interval"
:circular="circular"
:duration="duration"
:autoplay="autoplay"
:previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
:next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
:style="{ height: height + 'rpx', backgroundColor: bgColor }"
>
<swiper-item class="u-swiper-item" v-for="(item, index) in list" :key="index">
<view
class="u-list-image-wrap"
@tap.stop.prevent="listClick(index)"
:class="[uCurrent != index ? 'u-list-scale' : '']"
:style="{
borderRadius: `${borderRadius}rpx`,
transform: effect3d && uCurrent != index ? 'scaleY(0.9)' : 'scaleY(1)',
margin: effect3d && uCurrent != index ? '0 20rpx' : 0
}"
>
<image class="u-swiper-image" :src="item[name] || item" :mode="imgMode"></image>
<view
v-if="title && item.title"
class="u-swiper-title u-line-1"
:style="[{ 'padding-bottom': titlePaddingBottom }, titleStyle]"
>
{{ item.title }}
</view>
</view>
</swiper-item>
</swiper>
<view
class="u-swiper-indicator"
:style="{
top:
indicatorPos == 'topLeft' || indicatorPos == 'topCenter' || indicatorPos == 'topRight'
? '12rpx'
: 'auto',
bottom:
indicatorPos == 'bottomLeft' || indicatorPos == 'bottomCenter' || indicatorPos == 'bottomRight'
? '12rpx'
: 'auto',
justifyContent: justifyContent,
padding: `0 ${effect3d ? '74rpx' : '24rpx'}`
}"
>
<block v-if="mode == 'rect'">
<view
class="u-indicator-item-rect"
:class="{ 'u-indicator-item-rect-active': index == uCurrent }"
v-for="(item, index) in list"
:key="index"
></view>
</block>
<block v-if="mode == 'dot'">
<view
class="u-indicator-item-dot"
:class="{ 'u-indicator-item-dot-active': index == uCurrent }"
v-for="(item, index) in list"
:key="index"
></view>
</block>
<block v-if="mode == 'round'">
<view
class="u-indicator-item-round"
:class="{ 'u-indicator-item-round-active': index == uCurrent }"
v-for="(item, index) in list"
:key="index"
></view>
</block>
<block v-if="mode == 'number'">
<view class="u-indicator-item-number">{{ uCurrent + 1 }}/{{ list.length }}</view>
</block>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'u-swiper',
options: {
addGlobalClass: true,
// #ifndef MP-TOUTIAO
virtualHost: true,
// #endif
styleIsolation: 'shared'
}
};
</script>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { SwiperProps } from './types';
import { $u } from '../..';
/**
* swiper 轮播图
* @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用
* @tutorial https://uviewpro.cn/zh/components/swiper.html
* @property {Array} list 轮播图数据,见官网"基本使用"说明
* @property {Boolean} title 是否显示标题文字需要配合list参数见官网说明默认false
* @property {String} mode 指示器模式见官网说明默认round
* @property {String|Number} height 轮播图组件高度单位rpx默认250
* @property {String} indicator-pos 指示器的位置默认bottomCenter
* @property {Boolean} effect3d 是否开启3D效果默认false
* @property {Boolean} autoplay 是否自动播放默认true
* @property {String|Number} interval 自动轮播时间间隔单位ms默认2500
* @property {Boolean} circular 是否衔接播放见官网说明默认true
* @property {String} bg-color 背景颜色默认var(--u-bg-color)
* @property {String|Number} border-radius 轮播图圆角值单位rpx默认8
* @property {Object} title-style 自定义标题样式
* @property {String|Number} effect3d-previous-margin mode = true模式的情况下激活项与前后项之间的距离单位rpx默认50
* @property {String} img-mode 图片的裁剪模式详见image组件裁剪模式默认aspectFill
* @event {Function} click 点击轮播图时触发
* @example <u-swiper :list="list" mode="dot" indicator-pos="bottomRight"></u-swiper>
*/
const props = defineProps(SwiperProps);
const emit = defineEmits(['click', 'change']);
// 当前活跃的swiper-item的index
const uCurrent = ref(Number(props.current));
// 监听list变化重置uCurrent值避免溢出
watch(
() => props.list,
(nVal, oVal) => {
if (nVal.length !== oVal.length) uCurrent.value = 0;
}
);
// 监听外部current的变化实时修改内部依赖于此测uCurrent值
watch(
() => props.current,
n => {
uCurrent.value = Number(n);
}
);
// 容器 justifyContent
const justifyContent = computed(() => {
if (props.indicatorPos == 'topLeft' || props.indicatorPos == 'bottomLeft') return 'flex-start';
if (props.indicatorPos == 'topCenter' || props.indicatorPos == 'bottomCenter') return 'center';
if (props.indicatorPos == 'topRight' || props.indicatorPos == 'bottomRight') return 'flex-end';
return 'center';
});
// 标题下边距
const titlePaddingBottom = computed(() => {
if (props.mode == 'none') return '12rpx';
if (['bottomLeft', 'bottomCenter', 'bottomRight'].includes(props.indicatorPos) && props.mode == 'number') {
return '60rpx';
} else if (['bottomLeft', 'bottomCenter', 'bottomRight'].includes(props.indicatorPos) && props.mode != 'number') {
return '40rpx';
} else {
return '12rpx';
}
});
// swiper组件current参数只接受Number类型
const elCurrent = computed(() => Number(props.current));
/**
* 点击轮播图项
*/
function listClick(index: number) {
emit('click', index);
}
/**
* swiper change事件
*/
function change(e: any) {
const current = e.detail.current;
uCurrent.value = current;
emit('change', current);
}
/**
* swiper animationfinish事件
* 头条小程序不支持animationfinish事件改由change事件
* 暂不监听此事件因为不再给swiper绑定uCurrent属性
*/
function animationfinish(e: any) {
// #ifndef MP-TOUTIAO
// uCurrent.value = e.detail.current
// #endif
}
defineExpose({ listClick, change, animationfinish });
</script>
<style lang="scss" scoped>
@import '../../libs/css/style.components.scss';
.u-swiper-wrap {
position: relative;
overflow: hidden;
transform: translateY(0);
}
.u-swiper-image {
width: 100%;
will-change: transform;
height: 100%;
/* #ifndef APP-NVUE */
display: block;
/* #endif */
/* #ifdef H5 */
pointer-events: none;
/* #endif */
}
.u-swiper-indicator {
padding: 0 24rpx;
position: absolute;
@include vue-flex;
width: 100%;
z-index: 1;
}
.u-indicator-item-rect {
width: 26rpx;
height: 8rpx;
margin: 0 6rpx;
transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.3);
}
.u-indicator-item-rect-active {
background-color: rgba(255, 255, 255, 0.8);
}
.u-indicator-item-dot {
width: 14rpx;
height: 14rpx;
margin: 0 6rpx;
border-radius: 20rpx;
transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.3);
}
.u-indicator-item-dot-active {
background-color: rgba(255, 255, 255, 0.8);
}
.u-indicator-item-round {
width: 14rpx;
height: 14rpx;
margin: 0 6rpx;
border-radius: 20rpx;
transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.3);
}
.u-indicator-item-round-active {
width: 34rpx;
background-color: rgba(255, 255, 255, 0.8);
}
.u-indicator-item-number {
padding: 6rpx 16rpx;
line-height: 1;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 100rpx;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
}
.u-list-scale {
transform-origin: center center;
}
.u-list-image-wrap {
width: 100%;
height: 100%;
flex: 1;
transition: all 0.5s;
overflow: hidden;
box-sizing: content-box;
position: relative;
}
.u-swiper-title {
position: absolute;
background-color: rgba(0, 0, 0, 0.3);
bottom: 0;
left: 0;
width: 100%;
font-size: 28rpx;
padding: 12rpx 24rpx;
color: rgba(255, 255, 255, 0.9);
}
.u-swiper-item {
@include vue-flex;
overflow: hidden;
align-items: center;
}
</style>