当前位置:   article > 正文

使用Vue写一个图片轮播组件_4.创建一个vue组件,用于显示一个图片轮播图,并实现自动播放和手动切换图片的功能

4.创建一个vue组件,用于显示一个图片轮播图,并实现自动播放和手动切换图片的功能

在制作这个组件之前,笔者google了不少关于轮播的文章,发现实现一个轮播的思路虽然各有不同,但是大的逻辑其实差不多,本文主要依据慕课网上焦点轮播图特效这节课,不过慕课网主要用原生JS写,而笔者则用Vue进行了重构,并且进行了一点修改。完成后的组件效果图如下:

 

 

一、理清思路,理解需求和原理

1. 要写一个什么样的轮播?

  • 在点击右侧箭头时,图片向左滑动到下一张;点击左侧箭头时,图片向右滑到下一张
  • 点击下面的小圆点,滑到对应的图片,相应小圆点的样式也发生改变
  • 要有过渡效果,要缓缓滑动过去
  • 当鼠标hover到图片上时,轮播暂停,当鼠标leave时,轮播继续
  • 自动播放功能
  • 无限滚动,即在滚动到最后一张时,再点击下一张时会继续向左滑动到第一张,而不是整个拉到第一张,这里有点难

2. 理解无限轮播的原理

我们先看下原理图:

 

 

图中红线区域即是我们看到的图片,这个轮播只展示5张图片,但是在它的首尾各还有两张图片,在图1前面放置了图5,在图5后面放置了图1,之所以这么做,是为了做无限滚动。无限滚动的原理在于:当整个图向左侧滚动到右边的图5时,会继续向前走到图1,在完全显示出图1后,会以肉眼看不到的速度向右侧拉回到最左边的图1。 这样,即使再向左侧滑动看到的就是图2了。

如下图:在最后的图1完成过渡完全显示出来后,再将整个列表瞬间向右拉到左侧的图1。另一张边界图图5的滚动也是,不过方向相反。

 

 

 

 

二、先让图片切换起来

1. 布局和准备

  1. <template>
  2. <div id="slider">
  3. <div class="window"> // window上图中红线框
  4. <ul class="container" :style="containerStyle"> //注意这里的:style //这是图片列表,排成一排
  5. <li> //列表最前面的辅助图,它和图5一样,用于无限滚动
  6. <img :src="sliders[sliders.length - 1].img" alt="">
  7. </li>
  8. <li v-for="(item, index) in sliders" :key="index"> //通过v-for渲染的需要展示的5张图
  9. <img :src="item.img" alt="">
  10. </li>
  11. <li> //列表最后面的辅助图,它和图1一样,用于无限滚动
  12. <img :src="sliders[0].img" alt="">
  13. </li>
  14. </ul>
  15. <ul class="direction"> //两侧的箭头
  16. <li class="left">
  17. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z" /></svg>
  18. </li>
  19. <li class="right">
  20. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M557.179 904c-8.189 0-16.379-3.124-22.628-9.372-12.496-12.497-12.496-32.759 0-45.256L871.924 512 534.551 174.627c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0l360 360c12.496 12.497 12.496 32.758 0 45.255l-360 360c-6.249 6.249-14.439 9.373-22.628 9.373z" /></svg>
  21. </li>
  22. </ul>
  23. <ul class="dots"> //下面的小圆点
  24. <li v-for="(dot, i) in sliders" :key="i"
  25. :class="{dotted: i === (currentIndex-1)}"
  26. >
  27. </li>
  28. </ul>
  29. </div>
  30. </div>
  31. </template>
  32. <script>
  33. export default {
  34. name: 'slider',
  35. data () {
  36. return {
  37. sliders:[
  38. {
  39. img:'../../static/images/1.jpg'
  40. },
  41. {
  42. img:'../../static/images/2.jpg'
  43. },
  44. {
  45. img:'../../static/images/3.jpg'
  46. },
  47. {
  48. img:'../../static/images/4.jpg'
  49. },
  50. {
  51. img:'../../static/images/5.jpg'
  52. }
  53. ],
  54. currentIndex:1,
  55. distance:-600
  56. }
  57. },
  58. computed:{
  59. containerStyle() { //这里用了计算属性,用transform来移动整个图片列表
  60. return {
  61. transform:`translate3d(${this.distance}px, 0, 0)`
  62. }
  63. }
  64. }
  65. }
  66. </script>

好了,布局大概就是这样,效果图如下:

 

上面的代码已经做了注释,有几个点在这里再提一下:

  • window是红线框,宽度为600px,它不会动,移动的是包裹着图片的container,它的移动方式用:style="containerStyle",这是一个计算属性,用transform:translate3d(${this.distance, 0, 0})来控制左右移动
  • data里的distancecurrentIndex是关键,distance控制着移动的距离,默认是-600,显示7张图片中的第二张,也就是图1。currentIndex是window显示的图片的索引,这里默认是1,也是7张图片中第2张。
  • 需要展示的只有5张图片,但是在图1前了一张图5、在图5后面放了一张图1来做无限滚动,原理前面说过了
  • 当点击右侧的箭头,container向左移动,distance会越来越小;当点击左侧的箭头,container向右移动,distance会越来越大,方向不要弄错

2. 图片切换

我们在左侧和右侧的箭头上添加点击事件:

  1. <ul class="direction">
  2. <li class="left" @click="move(600, 1)">
  3. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z" /></svg>
  4. </li>
  5. <li class="right" @click="move(600, -1)">
  6. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M557.179 904c-8.189 0-16.379-3.124-22.628-9.372-12.496-12.497-12.496-32.759 0-45.256L871.924 512 534.551 174.627c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0l360 360c12.496 12.497 12.496 32.758 0 45.255l-360 360c-6.249 6.249-14.439 9.373-22.628 9.373z" /></svg>
  7. </li>
  8. </ul>
  9. ......
  10. methods:{
  11. move(offset, direction) {
  12. this.distance += this.distance * direction
  13. if (this.distance < -3000) this.distance = -600
  14. if (this.distance > -600) this.distance = -3000
  15. }
  16. }

解释下上面的代码:点击左侧或者右侧的箭头,调用move函数,move接收偏移量offset和方向direction两个参数。direction只传两个值,1表示container向右移动,-1表示container向左移动;偏移量是600,也就是一张图片的宽度。如果移动到7张图片的最后一张,就把container拉到7张图片里的第二张;如果移动到7张图片里第一张,就把container拉到7张图片里的第5张。

效果:

 

 

可以看到,图片切换效果已经出来了,但是下面的小圆点没有跟着变换。接下来我们把这个效果加上。从上面的html代码可以看到,:class="{dotted: i === (currentIndex - 1)}",小圆点的切换效果和data里的currentIndex值相关,我们只要随着图片切换变动currentIndex值就可以了。

修改move方法里的代码:

  1. ......
  2. move(offset, direction) {
  3. direction === -1 ? this.currentIndex++ : this.currentIndex--
  4. if (this.currentIndex > 5) this.currentIndex = 1
  5. if (this.currentIndex < 1) this.currentIndex = 5
  6. this.distance = this.distance + offset * direction
  7. if (this.distance < -3000) this.distance = -600
  8. if (this.distance > -600) this.distance = -3000
  9. }

上面的添加的三行代码很好理解,如果是点击右侧箭头,container就是向左移动,this.currentIndex就是减1,反之就是加1。

效果:

 

 

可以看到,小圆点的切换效果已经出来了。

三、过渡动画

上面的代码已经实现了切换,但是没有动画效果,显的非常生硬,接下来就是给每个图片的切换过程添加过渡效果。

这个轮播组件笔者并没有使用Vue自带的class钩子,也没有直接使用css的transition属性,而是用慕课网原作者讲的setTimeout方法加递归来实现。

其实我也试过使用Vue的钩子,但是总有一些小问题解决不掉;比如下面找到的这个例子:例子

这个例子在过渡的边界上有一些问题,我也遇到了,而且还是时有时无。而如果使用css的transition过渡方法,在处理边界的无限滚动上总会在chrome浏览器上有一下闪动,即使添加了-webkit-transform-style:preserve-3d;-webkit-backface-visibility:hidden也还是没用,而且要配合transition的transitionend事件对于IE浏览器的支持也不怎么好。

如果大家有看到更好的办法,请在评论中留言哦~

下面我们来写这个过渡效果,主要是改写:

  1. methods:{
  2. move(offset, direction) {
  3. direction === -1 ? this.currentIndex++ : this.currentIndex--
  4. if (this.currentIndex > 5) this.currentIndex = 1
  5. if (this.currentIndex < 1) this.currentIndex = 5
  6. const destination = this.distance + offset * direction
  7. this.animate(destination, direction)
  8. },
  9. animate(des, direc) {
  10. if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) {
  11. this.distance += 30 * direc
  12. window.setTimeout(() => {
  13. this.animate(des, direc)
  14. }, 20)
  15. } else {
  16. this.distance = des
  17. if (des < -3000) this.distance = -600
  18. if (des > -600) this.distance = -3000
  19. }
  20. }
  21. }

上面的代码是这个轮播我觉得最麻烦、也是最难理解的地方。

来理解一下:首先,我们对于move方法进行了改写,因为要一点点的移动,所以要先算出要移动到的目标距离。然后,我们写一个animate函数来实现这个过渡。这个animate函数接收两个参数,一个是要移动到的距离,另一个是方向。如果我们点击了右侧的箭头,container要向左侧移动,要是没有移动到目标距离,就在this.distance减去一定的距离,如果减去后还是没有到达,在20毫米以后再调用这个this.animate,如此不断移动,就形成了过渡效果。而如果移动到了目标距离,那就将目标距离赋值给this.distance,然后再进行边界和无限滚动的判断。

当然,使用window.setInterval()也可以实现这个效果,而且会稍微好理解一点,因为没有用到递归:

  1. methods:{
  2. move(offset, direction) {
  3. direction === -1 ? this.currentIndex++ : this.currentIndex--
  4. if (this.currentIndex > 5) this.currentIndex = 1
  5. if (this.currentIndex < 1) this.currentIndex = 5
  6. const destination = this.distance + offset * direction
  7. this.animate(destination, direction)
  8. },
  9. animate(des, direc) {
  10. const temp = window.setInterval(() => {
  11. if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) {
  12. this.distance += 30 * direc
  13. } else {
  14. window.clearInterval(temp)
  15. this.distance = des
  16. if (des < -3000) this.distance = -600
  17. if (des > -600) this.distance = -3000
  18. }
  19. }, 20)
  20. }
  21. }

实现出来的效果如下:

 

 

四、简单节流一下

写到这里,效果是出来了,但是会有一点问题,如果多次快速点击,就会有可能出现下面这种情况:

 

 

出现这种情况的原因很简单,因为是使用定时器过渡,所以连续快速点击就会出现错乱,简单节流一下就好了:在过渡完成之前点击箭头无效,其实就是设了一个闸,第一次点击把闸打开,在闸再次打开之前,让一部分代码无法执行,然后再在恰当的时机把闸打开。

我们把这个闸设在move函数里:

  1. move(offset, direction) {
  2. if (!this.transitionEnd) return //这里是闸
  3. this.transitionEnd = false //开闸以后再把闸关上
  4. direction === -1 ? this.currentIndex++ : this.currentIndex--
  5. if (this.currentIndex > 5) this.currentIndex = 1
  6. if (this.currentIndex < 1) this.currentIndex = 5
  7. const destination = this.distance + offset * direction
  8. this.animate(destination, direction)
  9. }

this.transitionEnd是这个闸的钥匙,我们把它放到data里:

this.transitionEnd: true

这个闸一开始默认的状态是开着的,第一次点击以后,这个闸就关上了,this.tranisitonEnd = false,在再次打开之前,后面的代码都执行不了。接下来就是在恰当的时机把这个闸打开,而这个恰当的时机就是过渡完成时,也就是在animate函数里:

  1. animate(des, direc) {
  2. if (this.temp) {
  3. window.clearInterval(this.temp)
  4. this.temp = null
  5. }
  6. this.temp = window.setInterval(() => {
  7. if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) {
  8. this.distance += 30 * direc
  9. } else {
  10. this.transitionEnd = true //闸再次打开
  11. window.clearInterval(this.temp)
  12. this.distance = des
  13. if (des < -3000) this.distance = -600
  14. if (des > -600) this.distance = -3000
  15. }
  16. }, 20)
  17. }

这下快速点击就没有之前的那个问题了:

 

 

五、点击小圆点实现图片过渡切换

到目前为止的代码:

  1. <template>
  2. <div id="slider">
  3. <div class="window">
  4. <ul class="container" :style="containerStyle">
  5. <li>
  6. <img :src="sliders[sliders.length - 1].img" alt="">
  7. </li>
  8. <li v-for="(item, index) in sliders" :key="index">
  9. <img :src="item.img" alt="">
  10. </li>
  11. <li>
  12. <img :src="sliders[0].img" alt="">
  13. </li>
  14. </ul>
  15. <ul class="direction">
  16. <li class="left" @click="move(600, 1)">
  17. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z" /></svg>
  18. </li>
  19. <li class="right" @click="move(600, -1)">
  20. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M557.179 904c-8.189 0-16.379-3.124-22.628-9.372-12.496-12.497-12.496-32.759 0-45.256L871.924 512 534.551 174.627c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0l360 360c12.496 12.497 12.496 32.758 0 45.255l-360 360c-6.249 6.249-14.439 9.373-22.628 9.373z" /></svg>
  21. </li>
  22. </ul>
  23. <ul class="dots">
  24. <li v-for="(dot, i) in sliders" :key="i"
  25. :class="{dotted: i === (currentIndex-1)}"
  26. >
  27. </li>
  28. </ul>
  29. </div>
  30. </div>
  31. </template>
  32. <script>
  33. export default {
  34. name: 'slider',
  35. data () {
  36. return {
  37. sliders:[
  38. {
  39. img:'../../static/images/1.jpg'
  40. },
  41. {
  42. img:'../../static/images/2.jpg'
  43. },
  44. {
  45. img:'../../static/images/3.jpg'
  46. },
  47. {
  48. img:'../../static/images/4.jpg'
  49. },
  50. {
  51. img:'../../static/images/5.jpg'
  52. }
  53. ],
  54. currentIndex:1,
  55. distance:-600,
  56. transitionEnd: true
  57. }
  58. },
  59. computed:{
  60. containerStyle() {
  61. return {
  62. transform:`translate3d(${this.distance}px, 0, 0)`
  63. }
  64. }
  65. },
  66. methods:{
  67. move(offset, direction) {
  68. if (!this.transitionEnd) return
  69. this.transitionEnd = false
  70. direction === -1 ? this.currentIndex++ : this.currentIndex--
  71. if (this.currentIndex > 5) this.currentIndex = 1
  72. if (this.currentIndex < 1) this.currentIndex = 5
  73. const destination = this.distance + offset * direction
  74. this.animate(destination, direction)
  75. },
  76. animate(des, direc) {
  77. if (this.temp) {
  78. window.clearInterval(this.temp)
  79. this.temp = null
  80. }
  81. this.temp = window.setInterval(() => {
  82. if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) {
  83. this.distance += 30 * direc
  84. } else {
  85. this.transitionEnd = true
  86. window.clearInterval(this.temp)
  87. this.distance = des
  88. if (des < -3000) this.distance = -600
  89. if (des > -600) this.distance = -3000
  90. }
  91. }, 20)
  92. }
  93. }
  94. }
  95. </script>

接下来我们要实现点击下面的小圆点来实现过渡和图片切换。

  1. <ul class="dots">
  2. <li v-for="(dot, i) in sliders" :key="i"
  3. :class="{dotted: i === (currentIndex-1)}"
  4. @click = jump(i+1)>
  5. </li>
  6. </ul>

在点击小圆点的时候我们调用jump函数,并将索引i+1传给它。这里需要特别注意,小圆点的索引和图片对应的索引不一致,图片共7张,而5个小圆点对应的是图片中中间的5张,所以我们才传i+1

  1. jump(index) {
  2. const direction = index - this.currentIndex >= 0 ? -1 : 1 //获取滑动方向
  3. const offset = Math.abs(index - this.currentIndex) * 600 //获取滑动距离
  4. this.move(offset, direction)
  5. }

上面的代码有一个问题,在jump函数里调用move方法,move里对于currentIndex的都是+1,而点击小圆点可能是将currentIndex加或者减好多个,所以要对move里的代码修改下:

direction === -1 ? this.currentIndex += offset/600 : this.currentIndex -= offset/600

改一行,根据offset算出currentIndex就行了。

但是又有一个问题,长距离切换速度太慢,如下:

 

所以我们需要控制一下速度,让滑动一张图片耗费的时间和滑动多张图片耗费的时间一样,给move和animate函数添加一个speed参数,还要再算一下:

  1. jump(index) {
  2. const direction = index - this.currentIndex >= 0 ? -1 : 1
  3. const offset = Math.abs(index - this.currentIndex) * 600
  4. const jumpSpeed = Math.abs(index - this.currentIndex) === 0 ? this.speed : Math.abs(index - this.currentIndex) * this.speed
  5. this.move(offset, direction, jumpSpeed)
  6. }

六、自动播放与暂停

前面的写的差不多了,到这里就非常简单了,写一个函数play:

  1. play() {
  2. if (this.timer) {
  3. window.clearInterval(this.timer)
  4. this.timer = null
  5. }
  6. this.timer = window.setInterval(() => {
  7. this.move(600, -1, this.speed)
  8. }, 4000)
  9. }

除了初始化以后自动播放,还要通过mouseover和mouseleave来控制暂停与播放:

  1. stop() {
  2. window.clearInterval(this.timer)
  3. this.timer = null
  4. }

七、 两处小坑

1. window.onblurwindow.onfocus

写到这里,基本功能都差不多了。但是如果把页面切换到别的页面,导致轮播图所在页面失焦,过一段时间再切回来会发现轮播狂转。原因是页面失焦以后,setInterval停止运行,但是如果切回来就会一次性把该走的一次性走完。解决的方法也很简单,当页面失焦时停止轮播,页面聚焦时开始轮播。

  1. window.onblur = function() { this.stop() }.bind(this)
  2. window.onfocus = function() { this.play() }.bind(this)

2. window.setInterval()小坑

当定时器window.setInterval()在多个异步回调中使用时,就有可能在某种机率下开启多个执行队列,所以为了保险起见,不仅应该在该清除时清除定时器,还要在每次使用之前也清除一遍

八、用props简单写两个对外接口

  1. props: {
  2. initialSpeed: {
  3. type: Number,
  4. default: 30
  5. },
  6. initialInterval: {
  7. type: Number,
  8. default: 4
  9. }
  10. },
  11. data() {
  12. ......
  13. speed: this.initialSpeed
  14. },
  15. computed:{
  16. interval() {
  17. return this.initialInterval * 1000
  18. }
  19. }

然后再在相应的地方修改下就可以了。

完整的代码如下:

  1. <template>
  2. <div id="slider">
  3. <div class="window" @mouseover="stop" @mouseleave="play">
  4. <ul class="container" :style="containerStyle">
  5. <li>
  6. <img :src="sliders[sliders.length - 1].img" alt="">
  7. </li>
  8. <li v-for="(item, index) in sliders" :key="index">
  9. <img :src="item.img" alt="">
  10. </li>
  11. <li>
  12. <img :src="sliders[0].img" alt="">
  13. </li>
  14. </ul>
  15. <ul class="direction">
  16. <li class="left" @click="move(600, 1, speed)">
  17. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z" /></svg>
  18. </li>
  19. <li class="right" @click="move(600, -1, speed)">
  20. <svg class="icon" width="30px" height="30.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M557.179 904c-8.189 0-16.379-3.124-22.628-9.372-12.496-12.497-12.496-32.759 0-45.256L871.924 512 534.551 174.627c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0l360 360c12.496 12.497 12.496 32.758 0 45.255l-360 360c-6.249 6.249-14.439 9.373-22.628 9.373z" /></svg>
  21. </li>
  22. </ul>
  23. <ul class="dots">
  24. <li v-for="(dot, i) in sliders" :key="i"
  25. :class="{dotted: i === (currentIndex-1)}"
  26. @click = jump(i+1)
  27. >
  28. </li>
  29. </ul>
  30. </div>
  31. </div>
  32. </template>
  33. <script>
  34. export default {
  35. name: 'slider',
  36. props: {
  37. initialSpeed: {
  38. type: Number,
  39. default: 30
  40. },
  41. initialInterval: {
  42. type: Number,
  43. default: 4
  44. }
  45. },
  46. data () {
  47. return {
  48. sliders:[
  49. {
  50. img:'../../static/images/1.jpg'
  51. },
  52. {
  53. img:'../../static/images/2.jpg'
  54. },
  55. {
  56. img:'../../static/images/3.jpg'
  57. },
  58. {
  59. img:'../../static/images/4.jpg'
  60. },
  61. {
  62. img:'../../static/images/5.jpg'
  63. }
  64. ],
  65. currentIndex:1,
  66. distance:-600,
  67. transitionEnd: true,
  68. speed: this.initialSpeed
  69. }
  70. },
  71. computed:{
  72. containerStyle() {
  73. return {
  74. transform:`translate3d(${this.distance}px, 0, 0)`
  75. }
  76. },
  77. interval() {
  78. return this.initialInterval * 1000
  79. }
  80. },
  81. mounted() {
  82. this.init()
  83. },
  84. methods:{
  85. init() {
  86. this.play()
  87. window.onblur = function() { this.stop() }.bind(this)
  88. window.onfocus = function() { this.play() }.bind(this)
  89. },
  90. move(offset, direction, speed) {
  91. if (!this.transitionEnd) return
  92. this.transitionEnd = false
  93. direction === -1 ? this.currentIndex += offset/600 : this.currentIndex -= offset/600
  94. if (this.currentIndex > 5) this.currentIndex = 1
  95. if (this.currentIndex < 1) this.currentIndex = 5
  96. const destination = this.distance + offset * direction
  97. this.animate(destination, direction, speed)
  98. },
  99. animate(des, direc, speed) {
  100. if (this.temp) {
  101. window.clearInterval(this.temp)
  102. this.temp = null
  103. }
  104. this.temp = window.setInterval(() => {
  105. if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) {
  106. this.distance += speed * direc
  107. } else {
  108. this.transitionEnd = true
  109. window.clearInterval(this.temp)
  110. this.distance = des
  111. if (des < -3000) this.distance = -600
  112. if (des > -600) this.distance = -3000
  113. }
  114. }, 20)
  115. },
  116. jump(index) {
  117. const direction = index - this.currentIndex >= 0 ? -1 : 1
  118. const offset = Math.abs(index - this.currentIndex) * 600
  119. const jumpSpeed = Math.abs(index - this.currentIndex) === 0 ? this.speed : Math.abs(index - this.currentIndex) * this.speed
  120. this.move(offset, direction, jumpSpeed)
  121. },
  122. play() {
  123. if (this.timer) {
  124. window.clearInterval(this.timer)
  125. this.timer = null
  126. }
  127. this.timer = window.setInterval(() => {
  128. this.move(600, -1, this.speed)
  129. }, this.interval)
  130. },
  131. stop() {
  132. window.clearInterval(this.timer)
  133. this.timer = null
  134. }
  135. }
  136. }
  137. </script>


作者:limingru 原文链接
来源:掘金

github地址

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号