Good day, friends! I decided to return to the topic of sliders. This article served as the inspiration . An article about the image gallery generator with a built-in slider is here .The slider that we will write works on the principle of an endless card deck or a stack of images. The top image is discarded in one of three directions - left, right, or up. The discarded image is replaced by the following, and so on to infinity. You can upload your images or use the default images.To register gestures (touches) and move (drag and drop) hammer.js is used . This library allows you to detect both mouse clicks and finger touches.So let's go.The markup looks like this:<input type="file" multiple>
<button>build carousel</button>
<div></div>
We have an โinputโ for loading images, a button for creating a carousel and a container for cards.Add some styles:body {
margin: 0;
overflow: hidden;
}
div {
width: 100%;
height: 100vh;
position: relative;
}
img {
width: 320px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.95);
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.1);
}
Nothing special here: the images are 320px wide, centered on top of one another.We pass to JS. I will give the whole code. Do not be afraid of the number of lines, everything is quite simple, except, perhaps, calculating the positions and coordinates. Almost every line has a comment.let button = document.querySelector('button')
let input = document.querySelector('input')
let files
let i = 0
button.addEventListener('click', () => {
files = input.files
document.body.removeChild(input)
document.body.removeChild(button)
let board = document.querySelector('div')
let carousel = new Carousel(board)
})
class Carousel {
constructor(element) {
this.board = element
this.push()
i++
this.push()
this.handle()
}
handle() {
this.cards = this.board.querySelectorAll('img')
this.topCard = this.cards[this.cards.length - 1]
this.nextCard = this.cards[this.cards.length - 2]
if (this.cards.length > 0) {
this.topCard.style.transform =
'translate(-50%, -50%) rotate(0deg) scale(1)'
this.hammer = new Hammer(this.topCard)
this.hammer.add(new Hammer.Tap())
this.hammer.add(new Hammer.Pan({
position: Hammer.position_ALL,
threshold: 0
}))
this.hammer.on('tap', (e) => {
this.onTap(e)
})
this.hammer.on('pan', (e) => {
this.onPan(e)
})
}
}
onTap(e) {
let propX = (e.center.x - e.target.getBoundingClientRect().left) / e.target.clientWidth
let rotateY = 15 * (propX < 0.05 ? -1 : 1)
this.topCard.style.transition = 'transform 100ms ease-out'
this.topCard.style.transform =
'translate(-50%, -50%) rotateX(0deg) rotateY(' + rotateY + 'deg) scale(1)'
setTimeout(() => {
this.topCard.style.transform =
'translate(-50%, -50%) rotate(0deg) scale(1)'
}, 100)
}
onPan(e) {
if (!this.isPanning) {
this.isPanning = true
this.topCard.style.transition = null
if (this.nextCard) this.nextCard.style.transition = null
let style = window.getComputedStyle(this.topCard)
let mx = style.transform.match(/^matrix\((.+)\)$/)
this.startPosX = mx ? parseFloat(mx[1].split(', ')[4]) : 0
this.startPosY = mx ? parseFloat(mx[1].split(', ')[5]) : 0
let bounds = this.topCard.getBoundingClientRect()
this.isDraggingFrom =
(e.center.y - bounds.top) > this.topCard.clientHeight / 2 ? -1 : 1
}
let posX = e.deltaX + this.startPosX
let posY = e.deltaY + this.startPosY
let propX = e.deltaX / this.board.clientWidth
let propY = e.deltaY / this.board.clientHeight
let dirX = e.deltaX < 0 ? -1 : 1
let deg = this.isDraggingFrom * dirX * Math.abs(propX) * 45
let scale = (95 + (5 * Math.abs(propX))) / 100
this.topCard.style.transform =
'translateX(' + posX + 'px) translateY(' + posY + 'px) rotate(' + deg + 'deg) scale(1)'
if (this.nextCard) this.nextCard.style.transform =
'translate(-50%, -50%) rotate(0deg) scale(' + scale + ')'
if (e.isFinal) {
this.isPanning = false
let successful = false
this.topCard.style.transition = 'transform 200ms ease-out'
if (this.nextCard) this.nextCard.style.transition = 'transform 100ms linear'
if (propX > 0.25 && e.direction == Hammer.DIRECTION_RIGHT) {
successful = true
posX = this.board.clientWidth
} else if (propX < -0.25 && e.direction == Hammer.DIRECTION_LEFT) {
successful = true
posX = -(this.board.clientWidth + this.topCard.clientWidth)
} else if (propY < -0.25 && e.direction == Hammer.DIRECTION_UP) {
successful = true
posY = -(this.board.clientHeight + this.topCard.clientHeight)
}
if (successful) {
this.topCard.style.transform =
'translateX(' + posX + 'px) translateY(' + posY + 'px) rotate(' + deg + 'deg)'
setTimeout(() => {
this.board.removeChild(this.topCard)
i++
if (i === files.length) i = 0
this.push()
this.handle()
}, 200)
} else {
this.topCard.style.transform =
'translate(-50%, -50%) rotate(0deg) scale(1)'
if (this.nextCard) this.nextCard.style.transform =
'translate(-50%, -50%) rotate(0deg) scale(0.95)'
}
}
}
push() {
let card = document.createElement('img')
if (files.length === 0) {
card.src = 'https://picsum.photos/320/320/?random=' +
Math.round(Math.random() * 1000000) +
')'
} else {
card.src = URL.createObjectURL(files[i])
}
if (this.board.firstChild) {
this.board.insertBefore(card, this.board.firstChild)
} else {
this.board.append(card)
}
}
}
โ GithubThank you for your attention.