轮播图是一个老生常谈的问题啊~本文提供一个功能齐全,但是样式简陋的轮播图。
主要功能有:
- 点击radio跳转
- 上一页/下一页 按钮点击
- 左滑动和右滑动(F12移动模式可以试)
- 自动播放
- 无限滚动 虽然样子简陋了点,但是功能还可。
思路
先说一下思路吧,假设有4个图要轮播1->2->3->4。那么安排dom(本文使用div)的时候,要这么安排:4->1->2->3->4->1。
为什么这么搞? 因为要实现无限滚动呐!比如,当前位置是图1,那么左移到图1前的图4,再使用点技巧定位到真·图4,即3后面的图4,这样看起来就像是无限滚动了。说实话,不要无限滚动代码会好写很多。
本文使用的技巧是使用transitioned监听器。(transition是一个css3属性,提供动画变化)。
本文全程使用css3变量控制动画!让JS最少的干涉css。
代码
template:
<template>
<div id="lab-swipe" ref="labRef">
<!-- imgs -->
<div id="imgs" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touched($event)">
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
</div>
<!-- 选择控制区 -->
<div id="radios">
<!-- name="swipe" -->
<input type="radio" name="radio"
v-for="i in 4" @click="c(i)" :key="i" :checked="i == checked"/>
</div>
<div id="buttons">
<button @click="before">上一页</button>
<button @click="after">下一页</button>
</div>
</div>
</template>
ts:script lang="ts"
import { onMounted, ref, watch, watchEffect } from "vue";
export default {
setup() {
const labRef = ref<any>();
let sum = 4;
const transition = (pos: number) => {
labRef.value;
let width = getComputedStyle(labRef.value as Element).getPropertyValue("--width");
let width_num = parseInt(width.split("px")[0]);
labRef.value.style.setProperty(`--pos`, -1 * pos * width_num + "px");
};
const c = (i: number) => {
let basewidth = 200;
console.log(i);
// 注入灵魂
now.value = i;
transition(i);
};
const now = ref(1);
// 为了在动画完成后radio再变化!
const checked=ref(1);
let beforeReset=false;
let afterReset =false;
onMounted(() => {
// 初始化位置
transition(1);
// 完成动画后.....完全是为了复位,真的令人头大...
(labRef.value as Element).addEventListener("transitionend", () => {
checked.value = now.value
if (!afterReset&&!beforeReset)return
labRef.value.style.setProperty(`--time`, "0s");
if (afterReset){
transition(1)
now.value = 1;
}else{
transition(sum)
now.value=sum
}
setTimeout(() => {
labRef.value.style.setProperty(`--time`, "1s");
afterReset= false;
beforeReset=false;
checked.value = now.value
}, 0);
});
// 设置自动播放呢~
autoDisplay()
});
let timer=-1;
const autoDisplay=()=>{
if (timer!=-1){
clearInterval(timer)
}
timer = setInterval(()=>{
after()
},4000)
}
// 之前
const after = () => {
if (now.value == sum) {
// 让侦听器做一些操作
afterReset=true;
transition(sum+1)
} else {
now.value++;
transition(now.value)
}
};
// 之后
const before = () => {
if (now.value == 1) {
// 让侦听器做一些操作
beforeReset=true;
transition(0)
} else {
now.value--;
transition(now.value)
}
};
// 应对触摸事件
let startTime;
let lastPos:number;
let startX:number;
let startY:number;
const touchStart= (e:any)=>{
//取消自动播放
clearInterval(timer);
timer=-1
startTime = new Date().getTime();
if (e.touches.length=== 1){
startX = e.touches[0].clientX // 记录开始位置
startY = e.touches[0].clientY // 记录开始位置
}
let pos = getComputedStyle(labRef.value as Element).getPropertyValue("--pos");
let pos_num = parseInt(pos.split("px")[0]);
lastPos = pos_num;
}
// 根据距离移动位置
const move=(dis:number)=>{
labRef.value.style.setProperty(`--pos`,lastPos+dis*-1+"px");
}
let moveX:number;
const touchMove=(e:any)=>{
//记录移动位置
moveX = e.touches[0].clientX
let disX = startX - moveX
// 冻结动画效果
labRef.value.style.setProperty(`--time`,"0s");
// 得到pos值
move(disX)
}
// 最后判定位置
const touched=(e:any)=>{
// 释放动画效果
labRef.value.style.setProperty(`--time`,"1s");
let width = getComputedStyle(labRef.value as Element).getPropertyValue("--width");
let width_num = parseInt(width.split("px")[0]);
let dis = moveX - startX;
if (dis>width_num/3){
before()
}
if (dis<-width_num/3){
after()
}
//开始动画
autoDisplay()
}
return {
c,
labRef,
after,
before,
now,
checked,
touchMove,
touchStart,
touched
};
},
};
scss:style lang="scss"
#lab-swipe {
position: relative;
display: flex;
flex-flow: nowrap row;
overflow: hidden;
--width: 200px;
--pos: -200px;
width: var(--width);
//初始--pos:反正会初始化的~不是0,在onMounted里
--pos: 0;
--time: 1s;
height: 150px;
#imgs {
display: flex;
flex-wrap: nowrap row;
transform: translateX(var(--pos));
transition: transform var(--time) ease 0s;
div {
//position: relative;
width: 200px;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
flex-shrink: 0;
}
div:nth-of-type(2n) {
background-color: rgb(238, 180, 238);
//position: -;
}
div:nth-of-type(2n + 1) {
background-color: rgb(209, 209, 110);
}
}
#radios {
width: 100px;
height: 20px;
position: absolute;
margin-left: auto;
margin-right: auto;
text-align: center;
z-index: 10;
//background-color: brown;
bottom: 1rem;
left: 0;
right: 0;
}
#buttons {
text-align: center;
position: absolute;
z-index: 10;
left: 0;
right: 0;
top: 1rem;
}
}
css是真的没有做任何优化。。。主要重点是功能!