Vue 实现无缝轮播
很多网站都会有轮播图的需求,而简单的轮播图实现通常会在展现完最后一个子项后中止轮播,或者者跳回到第一个子项重复轮播过程,这样的交互效果往往是存在断层的。接下来详情如何实现一个无缝的轮播图,达到这样的效果:
预览地址:https://jsfiddle.net/JunreyCen/qxogapws/
核心思想其实非常简单:
当轮播到边界子项(
Item 3
),并继续进行横移时,把即将要展现的子项(Item 1
)挪到紧挨着Item 3
的位置,执行横移,如下图Step 1
;因为此时活跃子项的索引(
index > 2
)已经超出范围,在下一次横移进行前,需要把索引调整到正当范围内,并重置子项的位置,如下图Step 2
。注意,这一步需要把transition
关闭,不然 “偷梁换柱” 的过程会被一览无遗。
“偷梁换柱” 过程
这里提供一份完整的代码实现。我略微做了点优化,支持
- 左、右两个方向轮播
- 一次切换多个子项
原理上无非是支持多个子项的同时 “偷梁换柱” 罢了,详细的可以关注代码中的 next
函数。
<html> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <head> <style> ul { list-style: none; } .swipe { position: absolute; left: 0; right: 0; margin: 40px auto; width: 90%; max-width: 375px; height: 200px; overflow: hidden; } .swipe-group { display: flex; margin: 0; padding: 0; width: 100%; height: 100%; } .swipe-item { flex: 0 0 100%; height: 100%; line-height: 200px; text-align: center; font-size: 40px; font-weight: 600; color: #fff; } .swipe-item:nth-child(1) { background-color: aquamarine; } .swipe-item:nth-child(2) { background-color: chocolate; } .swipe-item:nth-child(3) { background-color: darksalmon; } </style> </head> <body> <div id="app"></div> <template id="tpl"> <div class="swipe"> <ul class="swipe-group" :style="groupStyle"> <li class="swipe-item" v-for="item in items" ref="item"> {{item}} </li> </ul> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', template: '#tpl', computed: { groupStyle() { return { 'transform': `translate3d(${this.offset}px, 0, 0)`, 'transition-duration': `${this.duration}ms`, }; }, }, data() { return { items: [1, 2, 3], index: 0, // 当前轮播项索引 offset: 0, // swipe组的偏移量 duration: 0, // 过渡动画时长 itemWidth: 0, // 轮播项宽度 }; }, mounted() { if (this.$el) { this.itemWidth = this.$el.getBoundingClientRect().width; } this.autoplay(); }, methods: { // index 超出范围时调整 correctIndex() { this.duration = 0; const total = this.items.length; if (this.index < 0) { this.next(total, true); } else if (this.index > total - 1) { this.next(-total, true); } }, // 移动到达目标 index 途中的所有 swipe-item moveItems(indexOffset) { const targetIndex = this.index + indexOffset; if (this.index < targetIndex) { // 向左 for (let i = this.index; i < targetIndex; i++) { this.moveItem(i + 1); } } else { // 向右 for (let i = targetIndex; i < this.index; i++) { this.moveItem(i); } } }, // 移动 swipe-item moveItem(index) { const total = this.items.length; const itemIndex = index % 3 < 0 ? index % 3 + 3 : index % 3; // 目标 index 超出范围时调整对应 swipe-item 的偏移值 if (index > total - 1) { this.$refs.item[itemIndex].style.transform = `translateX(${total * this.itemWidth}px)`; } else if (index < 0) { this.$refs.item[itemIndex].style.transform = `translateX(${-total * this.itemWidth}px)`; } else { this.$refs.item[itemIndex].style.transform = 'translateX(0px)'; } }, resetItems() { this.$refs.item.forEach($item => { $item.style.transform = 'translateX(0px)'; }); }, // 向左/右方向切换 indexOffset 个 swipe-item next(indexOffset, isCorrect) { isCorrect ? this.resetItems() : this.moveItems(indexOffset); this.index += indexOffset; this.offset = -this.index * this.itemWidth; }, autoplay() { this.player = setInterval(() => { this.duration = 0; this.correctIndex(); // 30ms延时是为了屏蔽 reset 过程中的过渡动画 setTimeout(() => { this.duration = 500; this.next(1); }, 30); }, 1000); }, }, }); </script> </body></html>
代码已发布在 github 上,欢迎大家提 issue 交流。
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Vue 实现无缝轮播
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Vue 实现无缝轮播