Menulis slider JavaScript yang rumit namun menarik



Selamat siang teman! Saya memutuskan untuk kembali ke topik slider. Artikel ini menjadi inspirasi . Artikel tentang generator galeri gambar dengan bilah geser internal ada di sini .

Slider yang akan kita tulis bekerja berdasarkan prinsip tumpukan kartu tanpa akhir atau setumpuk gambar. Gambar atas dibuang di salah satu dari tiga arah - kiri, kanan, atau atas. Gambar yang dibuang diganti dengan yang berikut, dan seterusnya hingga tak terbatas. Anda dapat mengunggah gambar Anda atau menggunakan gambar default.

Untuk mendaftarkan gerakan (menyentuh) dan bergerak (seret dan lepaskan) hammer.js digunakan . Perpustakaan ini memungkinkan Anda untuk mendeteksi klik mouse dan sentuhan jari.

Jadi ayo pergi.

Markupnya terlihat seperti ini:

<input type="file" multiple>
<button>build carousel</button>
<div></div>

Kami memiliki "input" untuk memuat gambar, tombol untuk membuat korsel dan wadah untuk kartu.

Tambahkan beberapa gaya:

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);
}

Tidak ada yang istimewa di sini: gambar berukuran 320px lebar, berpusat di atas satu sama lain.

Kami lolos ke JS. Saya akan memberikan seluruh kode. Jangan takut dengan jumlah garis, semuanya cukup sederhana, kecuali, mungkin, menghitung posisi dan koordinat. Hampir setiap baris memiliki komentar.

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

        //      Y (+/-15 )
        let rotateY = 15 * (propX < 0.05 ? -1 : 1)

        //    transition
        this.topCard.style.transition = 'transform 100ms ease-out'

        // 
        this.topCard.style.transform =
            'translate(-50%, -50%) rotateX(0deg) rotateY(' + rotateY + 'deg) scale(1)'

        //   
        setTimeout(() => {
            //    transform
            this.topCard.style.transform =
                'translate(-50%, -50%) rotate(0deg) scale(1)'
        }, 100)

    }

    //  ()
    onPan(e) {
        if (!this.isPanning) {
            this.isPanning = true

            //    transition
            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()

            //      ,  (1)   (-1)
            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

        //   ,  (-1)   (1)
        let dirX = e.deltaX < 0 ? -1 : 1

        //   ,  0  +/-45 
        let deg = this.isDraggingFrom * dirX * Math.abs(propX) * 45

        //    ,  95  100%
        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

            //    transition
            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)
        }
    }
}



β†’ Github

Terima kasih atas perhatian Anda.

All Articles