<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> #app { width: 1200px; height: 500px; border: 1px solid red; margin: 50px auto; } .swiper { width: 100%; height: 100%; position: relative; overflow: hidden; } .item { width: 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 { width: 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 { width: 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; width: 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 = 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); } } }; const debounce = (method, delay) => { // 防抖函数 let timer = null; let start = false; return function () { if (!start) { start = true; method.apply(this, arguments); timer = setTimeout(() => { start = false; }, delay); } } }; const activeLeft = debounce(() => { if (data.index > 0) { data.index-- } else { data.index = data.list.length - 1 } }, 300); const activeRight = debounce(() => { if (data.index < data.list.length - 1) { data.index++ } else { data.index = 0 } }, 300); const changeLabelsActive = throttle((index) => { data.index = index; }, 300); return { data, changeActive, removeActive, activeLeft, activeRight, changeLabelsActive } } }; // 挂载 let app = document.getElementById('app'); createApp().mount(App, app); </script> </body> </html>
基于Vue3.0的CSS3动画轮播
来源:https://www.cnblogs.com/QQPrincekin/p/12022746.html