vue轮播
vue使用transition实现无缝轮播
直接上代码:
<template>
<div class="page">
<div class="carousel">
<div class="inner">
<div v-for="(item, index) in imgUrls" :key="index">
<transition>
<div class="carousel-item" v-if="index === currentIndex">
<img :src="item" alt="" />
</div>
</transition>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "",
components: {},
data() {
return {
currentIndex: 0,
imgUrls: [
"https://img.hbhcdn.com/zhuanti/jh-img-orig-ga_1329336533799247872_600_300_25174.jpg",
"https://img.hbhcdn.com/zhuanti/jh-img-orig-ga_1329337053091831808_600_300_36103.jpg",
"https://img.hbhcdn.com/zhuanti/jh-img-orig-ga_1329337017066954752_600_300_37129.jpg",
],
};
},
methods: {
autoPlay() {
setInterval(() => {
this.setIndex();
}, 3000);
},
setIndex() {
this.currentIndex++;
if (this.currentIndex == this.imgUrls.length) {
this.currentIndex = 0;
}
},
},
mounted() {
this.autoPlay();
},
};
</script>
<style>
.carousel {
width: 600px;
height: 300px;
position: relative;
margin: 0 auto;
}
.inner {
position: relative;
width: 100%;
height: 100%;
overflow: hidden; /*必须*/
}
.carousel-item {
position: absolute; /*必须*/
top: 0;
left: 30px;
width: 100%;
height: 100%;
margin: 0 10px;
}
.v-enter-active,
.v-leave-active { /*进入和离开需要执行时间*/
transition: all 0.5s linear;
}
.v-enter-active {
transform: translateX(100%);
}
.v-enter-to {
transform: translateX(0);
}
.v-leave-active {
transform: translateX(0);
}
.v-leave-to {
transform: translateX(-100%);
}
</style>
后续扩展放向:
1. 封装组件
2.可拖拽
3.轮播样式可定制化
4.基础拓展-->跑马灯功能
vue封装轮播图--组件化
层级结构
SwiperItem.vue
<template> <div class="slide"> <slot></slot> </div> </template> <script> export default { name: "Slide", }; </script> <style scoped> .slide { width: 100%; flex-shrink: 0; } .slide img { width: 100%; } </style>
Swiper.vue
<template> <div id="hy-swiper"> <div class="swiper" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" > <slot></slot> </div> <slot name="indicator"> </slot> <div class="indicator"> <slot name="indicator" v-if="showIndicator && slideCount > 1"> <div v-for="(item, index) in slideCount" class="indi-item" :class="{ active: index === currentIndex - 1 }" :key="index" ></div> </slot> </div> </div> </template> <script> export default { name: "Swiper", props: { interval: { //多久滚动一张 type: Number, default: 3000, }, animDuration: { //延迟多久开始轮播 type: Number, default: 300, }, moveRatio: { //手指拖拽轮播图宽度25%才会切下一张 type: Number, default: 0.25, }, showIndicator: { //指示器 type: Boolean, default: true, }, }, data: function() { return { slideCount: 0, // 元素个数 totalWidth: 0, // swiper的宽度 swiperStyle: {}, // swiper样式 currentIndex: 1, // 当前的index scrolling: false, // 是否正在滚动 }; }, mounted: function() { // 1.操作DOM, 在前后添加Slide setTimeout(() => { this.handleDom(); // 2.开启定时器 this.startTimer(); }, 200); }, methods: { /** * 定时器操作 */ startTimer: function() { this.playTimer = window.setInterval(() => { this.currentIndex++; this.scrollContent(-this.currentIndex * this.totalWidth); }, this.interval); }, stopTimer: function() { window.clearInterval(this.playTimer); }, /** * 滚动到正确的位置 */ scrollContent: function(currentPosition) { // 0.设置正在滚动 this.scrolling = true; // 1.开始滚动动画 this.swiperStyle.transition = "transform " + this.animDuration + "ms"; this.setTransform(currentPosition); // 2.判断滚动到的位置 this.checkPosition(); // 4.滚动完成 this.scrolling = false; }, /** * 校验正确的位置 */ checkPosition: function() { window.setTimeout(() => { // 1.校验正确的位置 this.swiperStyle.transition = "0ms"; if (this.currentIndex >= this.slideCount + 1) { this.currentIndex = 1; this.setTransform(-this.currentIndex * this.totalWidth); } else if (this.currentIndex <= 0) { this.currentIndex = this.slideCount; this.setTransform(-this.currentIndex * this.totalWidth); } // 2.结束移动后的回调 this.$emit("transitionEnd", this.currentIndex - 1); }, this.animDuration); }, /** * 设置滚动的位置 */ setTransform: function(position) { this.swiperStyle.transform = `translate3d(${position}px, 0, 0)`; this.swiperStyle[ "-webkit-transform" ] = `translate3d(${position}px), 0, 0`; this.swiperStyle["-ms-transform"] = `translate3d(${position}px), 0, 0`; }, /** * 操作DOM, 在DOM前后添加Slide */ handleDom: function() { // 1.获取要操作的元素 let swiperEl = document.querySelector(".swiper"); let slidesEls = swiperEl.getElementsByClassName("slide"); // 2.保存个数 this.slideCount = slidesEls.length; // 3.如果大于1个, 那么在前后分别添加一个slide if (this.slideCount > 1) { let cloneFirst = slidesEls[0].cloneNode(true); let cloneLast = slidesEls[this.slideCount - 1].cloneNode(true); swiperEl.insertBefore(cloneLast, slidesEls[0]); swiperEl.appendChild(cloneFirst); this.totalWidth = swiperEl.offsetWidth; this.swiperStyle = swiperEl.style; } // 4.让swiper元素, 显示第一个(目前是显示前面添加的最后一个元素) this.setTransform(-this.totalWidth); }, /** * 拖动事件的处理 */ touchStart: function(e) { // 1.如果正在滚动, 不可以拖动 if (this.scrolling) return; // 2.停止定时器 this.stopTimer(); // 3.保存开始滚动的位置 this.startX = e.touches[0].pageX; }, touchMove: function(e) { // 1.计算出用户拖动的距离 this.currentX = e.touches[0].pageX; this.distance = this.currentX - this.startX; let currentPosition = -this.currentIndex * this.totalWidth; let moveDistance = this.distance + currentPosition; // 2.设置当前的位置 this.setTransform(moveDistance); }, touchEnd: function(e) { // 1.获取移动的距离 let currentMove = Math.abs(this.distance); // 2.判断最终的距离 if (this.distance === 0) { return; } else if ( this.distance > 0 && currentMove > this.totalWidth * this.moveRatio ) { // 右边移动超过0.5 this.currentIndex--; } else if ( this.distance < 0 && currentMove > this.totalWidth * this.moveRatio ) { // 向左移动超过0.5 this.currentIndex++; } // 3.移动到正确的位置 this.scrollContent(-this.currentIndex * this.totalWidth); // 4.移动完成后重新开启定时器 this.startTimer(); }, /** * 控制上一个, 下一个 */ previous: function() { this.changeItem(-1); }, next: function() { this.changeItem(1); }, changeItem: function(num) { // 1.移除定时器 this.stopTimer(); // 2.修改index和位置 this.currentIndex += num; this.scrollContent(-this.currentIndex * this.totalWidth); // 3.添加定时器 this.startTimer(); }, }, }; </script> <style scoped> #hy-swiper { overflow: hidden; position: relative; } .swiper { display: flex; } .indicator { display: flex; justify-content: center; position: absolute; width: 100%; bottom: 8px; } .indi-item { box-sizing: border-box; width: 8px; height: 8px; border-radius: 4px; background-color: #fff; line-height: 8px; text-align: center; font-size: 12px; margin: 0 5px; } .indi-item.active { background-color: rgba(212, 62, 46, 1); } </style>
index.js
import Swiper from './Swiper' import SwiperItem from './SwiperItem' export { Swiper, SwiperItem }
引入使用该组件
<template> <div> <swiper> <swiper-item v-for="item in banners" :key="item.goods_id"> <a :href="item.image_src"> <img :src="item.image_src" alt="" /> </a> </swiper-item> </swiper> </div> </template> <script> import { SwiperItem, Swiper } from "components/common/swiper"; //轮播图 export default { name: "HomeSwiper", props: { banners: { type: Array, default() { return []; }, }, }, components: { SwiperItem, Swiper }, data() { return {}; }, }; </script> <style scoped></style>
...