<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#app {
1200px;
height: 500px;
border: 1px solid red;
margin: 50px auto;
}
.swiper {
100%;
height: 100%;
position: relative;
overflow: hidden;
}
.item {
100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
overflow: hidden;
}
.active {
z-index: 2
}
.animating {
transition: transform .4s ease-in-out;
}
.item:nth-child(1) {
background: pink;
}
.item:nth-child(2) {
background: green;
}
.item:nth-child(3) {
background: blue;
}
.item:nth-child(4) {
background: red;
}
.item:nth-child(5) {
background: orange;
}
.text {
font-size: 50px;
text-align: center;
color: #fff;
line-height: 500px;
}
.button {
50px;
height: 100px;
background: #fff;
opacity: .6;
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 3;
}
.left {
left: 20px;
}
.right {
right: 20px;
}
.indicators {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
z-index: 3;
list-style: none;
margin: 0;
padding: 0;
height: 27px;
}
.labels {
30px;
height: 3px;
display: inline-block;
padding: 12px 4px;
list-style: none;
margin: 0;
opacity: 0.5;
}
.labels-active {
opacity: 1;
}
.labels > span {
display: block;
100%;
height: 100%;
background: #fff;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="./vue.global.js"></script>
<script>
// Composition API
const {reactive, onMounted, effect, createApp, onRenderTriggered, computed, watch, onBeforeMount, createComponent} = Vue;
const Item = createComponent({
template: `<div class="item text"
:class="{
'active':data.active,
'animating': data.animating
}"
:style="itemStyle()">
<slot></slot>
</div>`,
setup(props) {
let width = 0;
const data = reactive({
translate: 0,
active: false,
animating: false
});
onMounted(() => {
width = document.querySelector('.item').offsetWidth;
translateItem(props.index, props.active, props.length, props.oldIndex);
});
const itemStyle = () => {
const value = `translateX(${data.translate}px)`;
return {
transform: value
}
};
const calcTranslate = (index, active) => {
return width * (index - active);
};
const processIndex = (index, active, length) => {
if (active === 0 && index === length - 1) {
return -1;
} else if (active === length - 1 && index === 0) {
return length;
} else if (index < active - 1 && active - index >= length / 2) {
return length + 1;
} else if (index > active + 1 && index - active >= length / 2) {
return -2;
}
return index;
};
const translateItem = (index, active, length, oldIndex) => {
if (width === 0) {
return;
}
if (oldIndex !== undefined) {
data.animating = index === active || index === oldIndex;
}
if (index !== active && length > 2) {
index = processIndex(index, active, length);
}
data.active = index === active;
data.translate = calcTranslate(index, active);
};
effect(() => {
translateItem(props.index, props.active, props.length, props.oldIndex);
});
return {
data,
itemStyle
}
}
});
const App = {
template: `<div class="swiper"
@mouseenter.stop="removeActive()"
@mouseleave.stop="changeActive()">
<Item v-for="($item, $index) in data.list"
:key="$index"
:index="$index"
:oldIndex="data.oldIndex"
:length="data.list.length"
:active="data.index">
{{$index + 1}}
</Item>
<div class="button left"
@click="activeLeft()">
</div>
<div class="button right"
@click="activeRight()">
</div>
<ul class="indicators">
<li class="labels"
:class="{'labels-active':data.index === $index}"
v-for="($item, $index) in data.list"
@mouseenter="changeLabelsActive($index)">
<span></span>
</li>
</ul>
</div>`,
components: {Item},
setup() {
const data = reactive({
list: [1, 2, 3, 4, 5],
index: 0,
oldIndex: undefined,
time: 3000
});
watch(() => data.index, (val, oldVal) => {
data.oldIndex = oldVal;
});
let time = null;
const _initTimer = (time) => {
return setInterval(() => {
if (data.index < data.list.length - 1) {
data.index++;
} else {
data.index = 0;
}
}, time);
};
onMounted(() => {
time = _initTimer(data.time);
});
const changeActive = () => {
clearInterval(time);
time = _initTimer(data.time);
};
const removeActive = () => {
clearInterval(time);
};
// 节流函数
// 立即执行 在单位时间内只触发一次事件
const throttle = (method, delay) => {
let timer = null;
let start = false;
return function () {
if (!start) {
start = true;
method.apply(this, arguments);
timer = setTimeout(() => {
start = false;
}, delay);
}
};
};
// 防抖函数
// 延迟执行 在事件被触发单位时间内 又被触发 则重新计时
const debounce = (method, delay) => {
let timer = null;
let start = null;
return function () {
let now = Date.now();
if (!start) {
start = now;
}
if (now - start > delay) {
method.apply(this, arguments);
start = now;
} else {
clearTimeout(timer);
timer = setTimeout(() => {
method.apply(this, arguments);
}, delay);
start = now;
}
};
};
const activeLeft = throttle(() => {
if (data.index > 0) {
data.index--;
} else {
data.index = data.list.length - 1;
}
}, 400);
const activeRight = throttle(() => {
if (data.index < data.list.length - 1) {
data.index++;
} else {
data.index = 0;
}
}, 400);
const changeLabelsActive = debounce((index) => {
data.index = index;
}, 400);
return {
data,
changeActive,
removeActive,
activeLeft,
activeRight,
changeLabelsActive
}
}
};
// 挂载
let app = document.getElementById('app');
createApp().mount(App, app);
</script>
</body>
</html>
基于Vue3.0的CSS3动画轮播