如何实现一个无缝无限轮播时间轴...
<p>废话不多说,先贴效果图<br><img src="https://img2024.cnblogs.com/blog/2997684/202504/2997684-20250410140418282-1364573635.png"></p>
<p>看图片可以把这个分割成两部分<br>
1.时间轴<br>
2.图片展示</p>
<ol>
<li><strong>首先来分析一下时间轴</strong><br>
可视范围这样<br>
<img src="https://img2024.cnblogs.com/blog/2997684/202504/2997684-20250410141328216-1573844891.png"><br>
实际长度<br>
<img src="https://img2024.cnblogs.com/blog/2997684/202504/2997684-20250410141521839-1416886125.png"><br>
操作<br>
给实际长度添加动画效果<br>
css部分<br>
<code>:style="{ '--translateX' : translateX }"</code><br>
<code>transform: translateX(calc(var(--translateX) * 1px));//1px是因为要用postcss转rem</code><br>
<code>transition: transform 0.6s ease-out;</code><br>
js部分<br>
<code>const translateX = ref(0);</code><br>
这就是主要实际长度的主要代码,是不是很简单translateX 的值根据自己需求偏移量计算</li>
<li><strong>接下来分析时间点:</strong><br>
分析一波:点主要是间隔分布,用间隔倍数来绝对定位确定每个点的位置<br>
<code>const dots = ref([])</code>点的数组<br>
<code>const dotSpacing = 251.5;</code>点间距<br>
<img src="https://img2024.cnblogs.com/blog/2997684/202504/2997684-20250410142901474-1313367953.png"><br>
css部分<br>
<code>:style="{'--x': ${index * dotSpacing}}"</code><br>
<code>left: calc(var(--x) * 1px);</code><br>
确定好样式了接下来就来实现点击轮播了<br>
先实现位置的重新计算direction 是传的值,1,-1表示左右🙂<br>
<code>const newPosition = Number((translateX.value + direction * dotSpacing));</code><br>
将值赋给偏移量就实现动画了<br>
<code>translateX.value = newPosition;</code><br>
是不是比较简单嘿!<br>
那怎么实现无限无缝的轮播呢?</li>
<li><strong>无限轮播</strong><br>
无缝循环轮播的要点就是:<br>
1.前后复制一组数据,避免出现空白<br>
2.在复制的数据在到达另一个边界的时候,回到原来的位置,利用屏幕刷新率,也就是帧率骗过用户的眼睛,达到无缝循环的视觉错觉<br>
<img src="https://img2024.cnblogs.com/blog/2997684/202504/2997684-20250410144042480-958395211.png"><br>
前后各复制一组数据<br>
<code>const clonedDots = [ ...dots.map(d => ({ ...d, cloned: true })), ...dots, ...dots.map(d => ({ ...d, cloned: true })), ];</code><br>
<code> const newPosition = Number((translateX.value + direction * dotSpacing));</code>获得偏移量<br>
<code>if (newPosition <= -(dots.length * dotSpacing))</code>如果b到达a的边界<br>
<code> translateX.value = newPosition</code>继续进行偏移<br>
<code>translateX.value = 0;</code>偏移完成后回到原点<br>
<code>transition: transform 0.6s ease-out</code>因为我们设置了过渡效果,所以会有动画回到原点<br>
那么我们就应该在b到达a点后禁用过渡立即重置,这里使用的是定时器,然后我们在下一帧的时候重新启用过渡效果<br>
<code>setTimeout(() => {</code><br>
<code>trackRef.value!.style.transition = 'transform 0s';</code><br>
<code>translateX.value = 0;</code><br>
<code> setTimeout(() => {</code><br>
<code>trackRef.value!.style.transition = 'transform 0.6s ease-out';</code><br>
<code> },16);</code><br>
<code>},600);</code><br>
一般屏幕的刷新率都是16,也可以设置为0<br>
这样我们就实现了无缝循环了,是不是还可以,但还是有点小问题,用户快速点击还是会有瑕疵的<br>
所以我们应该添加一个防抖函数或者标识<br>
那我们怎么去给中间的点添加动态的样式呢?<br>
我这里用的是<br>
<code>const currentIndex = ref(2);</code><br>
根据我自己的需要设置默认第二个是在中间的<br>
这样就只需要累加的方式就可以了<br>
<code> direction < 0 ? currentIndex.value ++: currentIndex.value -- ;</code><br>
然后重置的也就是比较死板<br>
<code>currentIndex.value = 2;</code><br>
<code>currentIndex.value = dots.length + 1;</code><br>
不知道有没有什么更灵活的方式<br>
这样就完成了时间轴的绘制了👌👌👌<br>
然后底下的切换也是比较简单<br>
可以手写或者像我一样使用vue的组件<code>transition-group</code><br>
因为我们只需要展示三个就不用去复制了<br>
<code>const currentItem = computed(() => clonedDots);</code><br>
<code>const prevItem = computed(() => clonedDots);</code><br>
<code>const nextItem = computed(() => clonedDots);</code><br>
<code>const transitionName = ref('slide-next');</code><br>
然后用effect函数监听变化<br>
<code>watch(currentIndex, (newVal, oldVal) => { transitionName.value = newVal > oldVal ? 'slide-next' : 'slide-prev'; });</code><br>
剩下的就只需要css去动画就可以了</li>
</ol>
<details>
<summary>点击查看代码</summary>
<pre><code>.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);
}
</code></pre>
</details>
<p>到这里我们理顺了这个组件的实现方法😄</p><br><br>
来源:https://www.cnblogs.com/bk-at-bear/p/18818431
頁:
[1]