| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- <template>
- <div class="banner-box" :style="isIndicator ? 'position: relative' : ''">
- <swiper :circular="props.circular" :autoplay="props.autoplay" :interval="props.interval" :current="swiperIndex"
- :previous-margin="previousMargin" :next-margin="nextMargin" @change="swiperChange"
- @animationfinish="swiperFinish" :style="{ height: props.height, 'border-radius': props.radius }">
- <swiper-item v-for="(item, index) in bannerList" :key="index" @click="clickSwiper(item)">
- <template v-if="props.type == 1">
- <image :src="item[props.proxyField.url]" mode="scaleToFill" v-if="item.urlType == 'image'" />
- <video class="banner-video" :src="item[props.proxyField.url]"
- :poster="item[props.proxyField.poster]" object-fit="fill"
- v-if="item.urlType == 'video'"></video>
- </template>
- <template v-if="props.type == 2">
- <view class="main-card" :class="swiperIndex == index ? 'current-card' : 'side-card'" :style="{
- 'border-radius': props.radius,
- margin: swiperIndex == index ? `0 ${props.spacing}` : '',
- }">
- <image :src="item[props.proxyField.url]" mode="scaleToFill" v-if="item.urlType == 'image'" />
- <video class="banner-video" :src="item[props.proxyField.url]"
- :poster="item[props.proxyField.poster]" object-fit="fill"
- v-if="item.urlType == 'video'"></video>
- </view>
- </template>
- </swiper-item>
- </swiper>
- <view class="banner-indicator indicator1" v-if="isIndicator && props.indicatorType == 1">
- <view class="indicator-item" :class="{ 'indicator-item-active': indicatorIndex === swiperIndex }"
- v-for="(img, indicatorIndex) in bannerList.length" :key="indicatorIndex"
- @click="swiperIndex = indicatorIndex">
- </view>
- </view>
- <view class="banner-indicator indicator2" :style="`width: calc(${40}rpx * ${bannerList.length})`"
- v-if="isIndicator && props.indicatorType == 2">
- <div class="banner-indicator-box">
- <view class="indicator-item" :style="`transform: translateX(calc(${40}rpx * ${swiperIndex}))`"></view>
- </div>
- </view>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, computed, watch, getCurrentInstance } from 'vue';
- import { onLoad } from '@dcloudio/uni-app';
- const { proxy } = getCurrentInstance();
- const example = proxy;
- const props = defineProps({
- bannerList: { //轮播图数据
- type: Array,
- required: true,
- },
- proxyField: { // 代理字段
- type: Object,
- default: {
- url: 'imageUrl', // 图片或视频资源的字段
- poster: 'poster', // 视频封面的图片网络资源的字段
- urlType: '', // 资源类型(image/video)
- },
- },
- height: { // 轮播图高度
- type: String,
- default: '500rpx',
- },
- circular: { // 是否采用衔接滑动
- type: Boolean,
- default: true,
- },
- autoplay: { // 是否自动切换
- type: Boolean,
- default: true,
- },
- interval: { // 自动切换时间间隔
- type: Number,
- default: 3000,
- },
- type: { // 轮播模式(1:默认;2:卡片)
- type: String,
- default: '1',
- },
- previousMargin: { // 前边距
- type: String,
- default: '',
- },
- nextMargin: { // 后边距
- type: String,
- default: '',
- },
- spacing: { // 卡片间距
- type: String,
- default: '20rpx',
- },
- radius: { // 圆角
- type: String,
- default: '20rpx',
- },
- isIndicator: { // 是否显示指示器
- type: Boolean,
- default: true,
- },
- indicatorType: { // 指示器类型(1:点状;2:条状)
- type: String,
- default: '1',
- },
- });
- let bannerList = ref([]);
- let previousMargin = ref('');
- let nextMargin = ref('');
- let isIndicator = ref(''); // 是否显示指示器
- watch(
- () => props.bannerList,
- () => {
- isIndicator.value = props.isIndicator;
- let osName = uni.getDeviceInfo().osName;
- if (props.type == 2) {
- previousMargin.value = props.previousMargin || '60rpx';
- nextMargin.value = props.nextMargin || '60rpx';
- }
- if (props.bannerList) {
- bannerList.value = props.bannerList;
- if (bannerList.value.length) {
- bannerList.value = bannerList.value.map((item) => {
- let urlType = props.proxyField.urlType || judgeFileType(item[props.proxyField.url]);
- if (urlType == 'video' && osName == 'ios') {
- isIndicator.value = false;
- }
- return {
- ...item,
- urlType,
- };
- });
- }
- }
- },
- {
- immediate: true,
- }
- );
- // 根据url判断文件类型
- function judgeFileType(url) {
- let imageType = [
- 'jpg',
- 'jpeg',
- 'png',
- 'gif',
- 'bmp',
- 'tiff',
- 'tif',
- 'webp',
- 'heic',
- 'ico',
- 'svg',
- 'eps',
- 'ai',
- 'raw',
- 'psd',
- 'xcf',
- 'tga',
- 'dds',
- ];
- let videoType = [
- 'mp4',
- 'mkv',
- 'avi',
- 'mov',
- 'wmv',
- 'flv',
- 'webm',
- 'mpg',
- 'mpeg',
- '3gp',
- 'm4v',
- 'vob',
- 'rmvb',
- 'f4v',
- 'ts',
- 'ogv',
- 'mxf',
- 'asf',
- 'swf',
- ];
- if (imageType.includes(url.split('.').pop())) {
- return 'image';
- } else if (videoType.includes(url.split('.').pop())) {
- return 'video';
- }
- }
- const emit = defineEmits(['clickSwiper']);
- let swiperIndex = ref(0); // 轮播图当前所在滑块的 index
- // 轮播图切换
- function swiperChange(e) {
- if (props.type == 2) {
- swiperIndex.value = e.detail.current;
- }
- }
- // 轮播图切换
- function swiperFinish(e) {
- if (props.type == 1) {
- swiperIndex.value = e.detail.current;
- }
- }
- // 点击轮播图
- function clickSwiper(item) {
- emit('clickSwiper', item);
- }
- </script>
- <style lang="scss" scoped>
- .banner-box {
- swiper {
- overflow: hidden;
- swiper-item {
- display: grid;
- align-items: center;
- overflow: hidden;
- transform: rotate(0deg);
- -webkit-transform: rotate(0deg);
- image {
- width: 100%;
- height: 100%;
- }
- video {
- width: 100%;
- height: 100%;
- }
- }
- }
- .banner-video {
- overflow: hidden;
- transform: rotate(0deg);
- -webkit-transform: rotate(0deg);
- }
- .main-card {
- overflow: hidden;
- transition: all 0.6s;
- }
- .current-card {
- height: 100%;
- }
- .side-card {
- height: 80%;
- }
- .banner-indicator {
- position: absolute;
- bottom: 20rpx;
- left: 50%;
- transform: translateX(-50%);
- }
- .indicator1 {
- display: flex;
- justify-content: center;
- align-items: center;
- .indicator-item {
- width: 15rpx;
- height: 15rpx;
- background: #fff;
- border-radius: 50%;
- margin-right: 14rpx;
- }
- .indicator-item-active {
- background: #fdd100;
- }
- }
- .indicator2 {
- .banner-indicator-box {
- background: #d4d4d4;
- border-radius: 15rpx;
- height: 20rpx;
- position: relative;
- .indicator-item {
- position: absolute;
- width: 40rpx;
- height: 20rpx;
- background: #2d99a1;
- border-radius: 15rpx;
- transition: all 0.3s;
- }
- }
- }
- }
- </style>
|