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.$children
Get the root node of all child components$el
To 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 pass
this.$el
Get the component root node - Note the boundary values when switching
- Each time the subcomponents need to be shown
z-index
Set 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 issetTimeout
和 setInterval
Better performance. As to why it didn’t workCSS3
的 transition
Property, because sometimes the image stays after the transition1px
The 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
及$el
Such as the use of- The use of the slot.
- For animation transition
requestAnimationFrame
Notice 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!