This article uses Vue to write a rotation component, previously also written: use Vue to write a picture rotation component, but more trouble to write, this article will pay more attention to the use of Vue API.

I. Effect preview and overall design

A preview:

Component usage:

<Carousel>
  <CarouselItem>
    <div class="img-wrapper"><img src="1.jpg"></div>
  </CarouselItem>
  <CarouselItem>
    <div class="img-wrapper"><img src="2.jpg"></div>
  </CarouselItem>
  <CarouselItem>
    <div class="img-wrapper"><img src="3.jpg"></div>
  </CarouselItem>
  <CarouselItem>
    <div class="img-wrapper"><img src="4.jpg"></div>
  </CarouselItem>
</Carousel>
Copy the code

As you can see, it’s split into two parts, and every scrollable image in the window is wrapped in a CarouseItem, so it’s designed to pass through the parent componentthis.$childrenGet the root node of all child components$elTo facilitate subsequent operations.

Second, the basic layout

The layout of the CarouselItem is very simple. The inside of the CarouselItem is a slot, and the root element of the component needs absolute positioning. The code is as follows:

<template>
  <div class="carousel-item-container">
    <slot></slot>
  </div>
</template>
Copy the code

Carousel, the parent component, is laid out in three parts

  • Windows part
  • On both sides of the arrow
  • Below the dot

The code is as follows:

<template>
  <div class="carousel2-container">
    <div class="window">
      <slot></slot>
    </div>
    <div class="arrows">
      <i class="fa fa-angle-left"></i>
      <i class="fa fa-angle-right"></i>
    </div>
    <div class="dots">
      <span
        v-for="(child, index) in children"
        :key="index"
        :class="{active: index === currentIndex}"
        ></span>
    </div>
  </div>
</template>
Copy the code

Notice that the number of dots below is based on the number of children. Github’s corresponding commit is here.

Three, let the picture switch

Because the CarouselItem component is an absolute location, it is superimposed by default. Use this.$children in the Carousel parent to get all child instances and store them together.

init () {
  this.children = this.$children
  this.children[this.currentIndex].$el.style.zIndex = 10 // Put the first image at the top
},
Copy the code

Next add click events to the left and right arrows and the dots below, toggle by changing the Z-index value of the root node of each element of this.children.

  resetZIndex () {
    this.children.forEach(vm= > {
      vm.$el.style.zIndex = 0})},// Click the left arrow
  clickLeft () {
    const { children, currentIndex } = this
    this.currentIndex = currentIndex - 1 < 0 ? children.length - 1 : currentIndex - 1
    this.resetZIndex()
    this.children[this.currentIndex].$el.style.zIndex = 10
  },
  // Click the arrow on the right
  clickRight () {
    const { children, currentIndex } = this
    this.currentIndex = currentIndex + 1 > children.length - 1 ? 0 : currentIndex + 1
    this.resetZIndex()
    this.children[this.currentIndex].$el.style.zIndex = 10
  },
  // Click the point below
  jump (index) {
    this.currentIndex = index
    this.resetZIndex()
    this.children[this.currentIndex].$el.style.zIndex = 10
  }
Copy the code

The above code please pay attention to the following problems:

  • Each Vue component can passthis.$elGet the component root node
  • Note the boundary values when switching
  • Each time the subcomponents need to be shownz-indexSet the value high so that it is at the top, and set the other z-index low

The effect is as follows:

The full Github submission is here.

Add transition animation, click the left arrow first

This is where I find this component most troublesome. The transition of the component animation is usedrequestAnimationFrame, it issetTimeoutsetIntervalBetter performance. As to why it didn’t workCSS3transitionProperty, because sometimes the image stays after the transition1pxThe blank, not fully engaged.

Principle of animation transition:

The blue part is the window, which is setoverflow:hidden;As it begins its transition, assuming it wants to move to the left, move the next figure 2 to the far right, usingtransform:translateX(100%)With requestAnimationFrame, move Figure 1 and Figure 2 slowly to the left. When Figure 2 fully occupies the window, the animation ends.

Code implementation:


clickLeft () {
  const { children, currentIndex } = this
  this.currentIndex = currentIndex - 1 < 0 ? children.length - 1 : currentIndex - 1
  this.resetZIndex()
  this.children[this.currentIndex].$el.style.zIndex = 10
  // this.resetZIndex()
  // this.children[this.currentIndex].$el.style.zIndex = 10
  this.addAnimation(currentIndex, this.currentIndex)
},

// Add animation
addAnimation (currentIndex, nextIndex) {
  const currentDom = this.children[currentIndex].$el
  const nextDom = this.children[nextIndex].$el
  currentDom.style.zIndex = 10
  nextDom.style.zIndex = 10
  this.go(currentDom, nextDom)
},

// Implement the animation using requestAnimationFrame
go (currentDom, nextDom) {
  let currentDomPosition = 0
  let nextDomPosition = - 100.
  nextDom.style.transform = `translateX(${nextDomPosition}%) `
  const render = (a)= > {
    currentDomPosition += 2
    nextDomPosition += 2
    if (nextDomPosition > 0) {
      return
    }
    currentDom.style.transform = `translateX(${currentDomPosition}%) `
    nextDom.style.transform = `translateX(${nextDomPosition}%) `
    window.requestAnimationFrame(render)
  }
  // Start frame 1
  window.requestAnimationFrame(render)
}
Copy the code

Please note the above code:

  • RequestAnimation is similar to setTimeout
  • Notice the diagram that the current window displays, and the diagram that it will display next, along with their indexes and corresponding root nodes
  • The implementation of the animation is done by manipulating the DOM

The complete Github submission of the above part of the code is here

Click the right arrow to add animation

The method and principle are the same as above, but note: one is the difference in the direction, need to add a direction parameter, the second is the requestAnimationFrame transition stop condition needs to be changed.

The code is as follows:


// Click the arrow on the left
clickLeft () {
  const { children, currentIndex } = this
  this.currentIndex = currentIndex - 1 < 0 ? children.length - 1 : currentIndex - 1
  // Note that there is an extra direction parameter passed
  this.addAnimation(currentIndex, this.currentIndex, 1)},// Click the arrow on the right
clickRight () {
  const { children, currentIndex } = this
  this.currentIndex = currentIndex + 1 > children.length - 1 ? 0 : currentIndex + 1
  // Note that there is an extra direction parameter passed
  this.addAnimation(currentIndex, this.currentIndex, - 1)},// Add direction parameters
addAnimation (currentIndex, nextIndex, direction) {
  const currentDom = this.children[currentIndex].$el
  const nextDom = this.children[nextIndex].$el
  currentDom.style.zIndex = 10
  nextDom.style.zIndex = 10
  // The direction is passed to go
  this.go(currentDom, nextDom, direction)
},

// Implement the animation using requestAnimationFrame
go (currentDom, nextDom, direction) {
  let currentDomPosition = 0
  let nextDomPosition = - 100. * direction
  nextDom.style.transform = `translateX(${nextDomPosition}%) `
  const render = (a)= > {
    currentDomPosition += (2 * direction)
    nextDomPosition += (2 * direction)
    // Note the animation stop condition
    if ((direction === 1 && nextDomPosition > 0) || (direction === - 1 && nextDomPosition < 0)) {
      return
    }
    currentDom.style.transform = `translateX(${currentDomPosition}%) `
    nextDom.style.transform = `translateX(${nextDomPosition}%) `
    window.requestAnimationFrame(render)
  }
  / / the first frame
  window.requestAnimationFrame(render)
}

Copy the code

Please note the above code:

  • Direction is controlled by 1 and -1. When moving to the left, the next image should start on the far right of the window, and vice versa
  • Note that the conditions for stopping the animation have changed

The complete Github submission for this section of logic is here.

Click the animation transition in the lower part


jump (index) {
  if (index === this.currentIndex) return
  const current = this.currentIndex
  const direction = index > this.currentIndex ? - 1 : 1
  this.currentIndex = index
  this.addAnimation(current, index, direction)
},
Copy the code

This part is easy, just distinguish the index of the current graph from that of the next graph to be shown. The rest of the logic is reused.

This part of the Github submission record is here.

Seven, each time the animation is complete, give a callback

The purpose of this is to reset the z-index of the partial graph, etc.


go (currentDom, nextDom, direction) {
  let currentDomPosition = 0
  let nextDomPosition = - 100. * direction
  nextDom.style.transform = `translateX(${nextDomPosition}%) `
  const render = (a)= > {
    currentDomPosition += (2 * direction)
    nextDomPosition += (2 * direction)
    if ((direction === 1 && nextDomPosition > 0) || (direction === - 1 && nextDomPosition < 0)) {
      // Here it is
      this.onFinish()
      return
    }
    currentDom.style.transform = `translateX(${currentDomPosition}%) `
    nextDom.style.transform = `translateX(${nextDomPosition}%) `
    window.requestAnimationFrame(render)
  }
  / / the first frame
  window.requestAnimationFrame(render)
},

// Animation transition completed callback
onFinish () {
  this.children.forEach((vm, index) = > {
    if(index ! = =this.currentIndex) {
      vm.$el.style.zIndex = 0
      vm.$el.style.transform = 'translateX(0)'}})}Copy the code

The overall effect here:

Eight, auto play and mouse hover

This part is pretty easy


// Call in mounted
autoPlay () {
  if (this.timer) window.clearInterval(this.timer)
  this.timer = window.setInterval((a)= > {
    this.clickRight()
  }, 3000)},// Mouse hover
mouseEnter () {
  window.clearInterval(this.timer)
},

// The mouse moves away and starts to play automatically
mouseLeave () {
  this.autoPlay()
}

Copy the code

Here’s the Github submission record.

Use throttling to solve the bug of fast and frequent locations

At this point, there is a bug, as shown below:

This is caused by continuous clicking without completing the transition, and the solution is also very simple. As long as the current transition is not complete, then do not let the point, click the left arrow, right arrow, dot to change.


data () {
  return {
    // Add a tag to data
    canClick: true // Can I click it
  }
},

clickLeft () {
  // The next two lines
  if (!this.canClick) return
  this.canClick = false
  const { children, currentIndex } = this
  this.currentIndex = currentIndex - 1 < 0 ? children.length - 1 : currentIndex - 1
  this.addAnimation(currentIndex, this.currentIndex, 1)},...// Animation transition completed callback
onFinish () {
  this.children.forEach((vm, index) = > {
    if(index ! = =this.currentIndex) {
      vm.$el.style.zIndex = 0
      vm.$el.style.transform = 'translateX(0)'}})// Restore it after the transition
  this.canClick = true
},

Copy the code

Complete Github submission record.

Here we are, almost done! Here’s the summary:

  • The Carousel is split into two components, Carousel and CarouselItem
  • this.$children$elSuch as the use of
  • The use of the slot.
  • For animation transitionrequestAnimationFrameNotice the difference between the current picture and the next picture to be shown
  • Use throttling to solve the problem of frequent clicking
  • Autoplay, play time, and rotation speed are configurable and can be placed directly in props

Github address for this componentIn thisNote that both Carousel and Carousel2 are available in the address. I wrote the parent component twice for this article. Thank you for reading!

Finally, the author is looking for a job, the coordinate is Shanghai, ask for recommendation, Vue use a lot, React also will, Github resume here. Thank you, thank you!