如何实现一个无缝无限轮播时间轴

笔记哥 / 04-10 / 36点赞 / 0评论 / 607阅读
废话不多说,先贴效果图 ![](https://cdn.res.knowhub.vip/c/2504/10/52158778.png?G1cAAMTmtHFS4MR3G%2b1Am1g10WbAIo2gUsJ6vffu04i%2b38BQ%2f8w%2blp0Pv%2bljGaHWnIoSGJEVLgiLskpBgZOQNOaQQvR7Gg%3d%3d) 看图片可以把这个分割成两部分 1.时间轴 2.图片展示 1. **首先来分析一下时间轴** 可视范围这样 ![](https://cdn.res.knowhub.vip/c/2504/10/52aa3be1.png?G1cAAMTsdJxI8oLoNuqQ9k5xp82ARRpBpYT1ev5%2fn4vo%2fRwMjffsY%2fn58Js%2blhNaq8WUwMisCEFYVBIMUoLkmkzVmsQ9HQ%3d%3d) 实际长度 ![](https://cdn.res.knowhub.vip/c/2504/10/8dfc305f.png?G1cAAMTsdJxIPkKj26hD2jvFHc2ARRpBpYT1es9Z%2byb6fhcWjc9offr%2b8JvWp5PUehVTEpbMihDAUGSBpRqgKGYFkuMaDg%3d%3d) 操作 给实际长度添加动画效果 css部分 `:style="{ '--translateX' : translateX }"` `transform: translateX(calc(var(--translateX) * 1px));//1px是因为要用postcss转rem` `transition: transform 0.6s ease-out;` js部分 `const translateX = ref(0);` 这就是主要实际长度的主要代码,是不是很简单translateX 的值根据自己需求偏移量计算 2. **接下来分析时间点:** 分析一波:点主要是间隔分布,用间隔倍数来绝对定位确定每个点的位置 `const dots = ref([])`点的数组 `const dotSpacing = 251.5;`点间距 ![](https://cdn.res.knowhub.vip/c/2504/10/cea80870.png?G1cAAGSd87ygE2r9TjyqCQIJNAMWaQSVEtbrPWftG%2bD7g5E1P6P1GfvDb1qfAexu9VJg5IKKFAhJ2ZHUNJGQSDUvktcI) css部分 `:style="{'--x': ${index * dotSpacing}}"` `left: calc(var(--x) * 1px);` 确定好样式了接下来就来实现点击轮播了 先实现位置的重新计算direction 是传的值,1,-1表示左右🙂 `const newPosition = Number((translateX.value + direction * dotSpacing));` 将值赋给偏移量就实现动画了 `translateX.value = newPosition;` 是不是比较简单嘿! 那怎么实现无限无缝的轮播呢? 3. **无限轮播** 无缝循环轮播的要点就是: 1.前后复制一组数据,避免出现空白 2.在复制的数据在到达另一个边界的时候,回到原来的位置,利用屏幕刷新率,也就是帧率骗过用户的眼睛,达到无缝循环的视觉错觉 ![](https://cdn.res.knowhub.vip/c/2504/10/75df832e.png?G1YAAMTW3Dgp8EFJ22gDdWfqnTYDElkElRLW6917rpvo%2bwMMzU%2bvbcT68JfaRhDcr7MogWGsSF5YVFmhhZNbOdwgkmcP) 前后各复制一组数据 `const clonedDots = [ ...dots.map(d => ({ ...d, cloned: true })), ...dots, ...dots.map(d => ({ ...d, cloned: true })), ];` ` const newPosition = Number((translateX.value + direction * dotSpacing));`获得偏移量 `if (newPosition <= -(dots.length * dotSpacing))`如果b到达a的边界 ` translateX.value = newPosition`继续进行偏移 ` translateX.value = 0;`偏移完成后回到原点 `transition: transform 0.6s ease-out`因为我们设置了过渡效果,所以会有动画回到原点 那么我们就应该在b到达a点后禁用过渡立即重置,这里使用的是定时器,然后我们在下一帧的时候重新启用过渡效果 `setTimeout(() => {` `trackRef.value!.style.transition = 'transform 0s';` `translateX.value = 0;` ` setTimeout(() => {` `trackRef.value!.style.transition = 'transform 0.6s ease-out';` ` },16);` `},600);` 一般屏幕的刷新率都是16,也可以设置为0 这样我们就实现了无缝循环了,是不是还可以,但还是有点小问题,用户快速点击还是会有瑕疵的 所以我们应该添加一个防抖函数或者标识 那我们怎么去给中间的点添加动态的样式呢? 我这里用的是 `const currentIndex = ref(2);` 根据我自己的需要设置默认第二个是在中间的 这样就只需要累加的方式就可以了 ` direction < 0 ? currentIndex.value ++: currentIndex.value -- ;` 然后重置的也就是比较死板 `currentIndex.value = 2;` `currentIndex.value = dots.length + 1;` 不知道有没有什么更灵活的方式 这样就完成了时间轴的绘制了👌👌👌 然后底下的切换也是比较简单 可以手写或者像我一样使用vue的组件`transition-group` 因为我们只需要展示三个就不用去复制了 `const currentItem = computed(() => clonedDots[currentIndex.value]);` `const prevItem = computed(() => clonedDots[currentIndex.value - 1]);` `const nextItem = computed(() => clonedDots[currentIndex.value + 1]);` `const transitionName = ref('slide-next');` 然后用effect函数监听变化 `watch(currentIndex, (newVal, oldVal) => { transitionName.value = newVal > oldVal ? 'slide-next' : 'slide-prev'; });` 剩下的就只需要css去动画就可以了 点击查看代码 ```csharp .slide-next-enter-active, .slide-next-leave-active, .slide-prev-enter-active, .slide-prev-leave-active { transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94); transform-style: preserve-3d; /* 保持3D变换 */ } .slide-next-enter-from { transform: scale(0.8) translateX(500px) translateZ(-100px); } .slide-next-enter-to { transform: scale(1) translateX(0) translateZ(0); } .slide-next-leave-active { z-index: 0; /* 确保离开元素在下层 */ } .slide-next-leave-to { transform: scale(0.8) translateX(-500px) translateZ(-100px); } /* 上一张进入动画 */ .slide-prev-enter-from { transform: scale(0.8) translateX(-500px) translateZ(-100px); } .slide-prev-enter-to { transform: scale(1) translateX(0) translateZ(0); } .slide-prev-leave-active { z-index:0 !important; /* 确保离开元素在下层 */ } .slide-prev-leave-to { transform: scale(0.8) translateX(500px) translateZ(-100px); z-index: 0 !important; } /* 当前激活项样式 */ .swiper-item:not(.prev):not(.next) { z-index: 4; box-shadow: 0 10px 25px rgba(0,0,0,0.2); } /* 相邻项样式 */ .prev, .next { transform: scale(0.8) translateZ(-100px); } .prev { transform: scale(0.8) translateX(-500px) translateZ(-100px); } .next { transform: scale(0.8) translateX(500px) translateZ(-100px); } ``` 到这里我们就完成了这个组件