рдПрдХ рдЬрдЯрд┐рд▓ рд▓реЗрдХрд┐рди рджрд┐рд▓рдЪрд╕реНрдк рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╕реНрд▓рд╛рдЗрдбрд░ рд▓рд┐рдЦрдирд╛



рд╢реБрдн рджрд┐рди, рджреЛрд╕реНрддреЛрдВ! рдореИрдВрдиреЗ рд╕реНрд▓рд╛рдЗрдбрд░реНрд╕ рдХреЗ рд╡рд┐рд╖рдп рдкрд░ рд▓реМрдЯрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред рдЗрд╕ рд▓реЗрдЦ рдиреЗ рдкреНрд░реЗрд░рдгрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд┐рдпрд╛ ред рдПрдХ рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рд╕реНрд▓рд╛рдЗрдбрд░ рдХреЗ рд╕рд╛рде рдЫрд╡рд┐ рдЧреИрд▓рд░реА рдЬрдирд░реЗрдЯрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рд▓реЗрдЦ рдпрд╣рд╛рдВ рд╣реИ ред

рд╕реНрд▓рд╛рдЗрдбрд░ рдЬреЛ рд╣рдо рд▓рд┐рдЦреЗрдВрдЧреЗ, рд╡рд╣ рдЕрдВрддрд╣реАрди рдХрд╛рд░реНрдб рдбреЗрдХ рдпрд╛ рдЫрд╡рд┐рдпреЛрдВ рдХреЗ рдвреЗрд░ рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрдд рдкрд░ рдХрд╛рдо рдХрд░реЗрдЧрд╛ред рд╢реАрд░реНрд╖ рдЫрд╡рд┐ рдХреЛ рддреАрди рджрд┐рд╢рд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдореЗрдВ рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ - рдмрд╛рдПрдВ, рджрд╛рдПрдВ, рдпрд╛ рдКрдкрд░ред рдЫреЛрдбрд╝реА рдЧрдИ рдЫрд╡рд┐ рдХреЛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕реЗ рдмрджрд▓ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдЗрд╕реА рддрд░рд╣ рдЕрдирдВрдд рддрдХред рдЖрдк рдЕрдкрдиреА рдЫрд╡рд┐рдпреЛрдВ рдХреЛ рдЕрдкрд▓реЛрдб рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдЫрд╡рд┐рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдЗрд╢рд╛рд░реЛрдВ (рд╕реНрдкрд░реНрд╢) рдФрд░ рдореВрд╡ (рдбреНрд░реИрдЧ рдПрдВрдб рдбреНрд░реЙрдк) рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдереМрдбрд╝реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред рдпрд╣ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЖрдкрдХреЛ рдорд╛рдЙрд╕ рдХреНрд▓рд┐рдХ рдФрд░ рдлрд┐рдВрдЧрд░ рдЯрдЪ рджреЛрдиреЛрдВ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИред

рддреЛ рдЪрд▓рддреЗ рд╣реИрдВред

рдорд╛рд░реНрдХрдЕрдк рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:

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

рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд▓реЛрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ "рдЗрдирдкреБрдЯ" рд╣реИ, рдХрд╛рд░реНрдб рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдмрдЯрди рдФрд░ рдХрд╛рд░реНрдб рдХреЗ рд▓рд┐рдП рдПрдХ рдХрдВрдЯреЗрдирд░ред

рдХреБрдЫ рд╢реИрд▓рд┐рдпреЛрдВ рдХреЛ рдЬреЛрдбрд╝реЗрдВ:

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

рдпрд╣рд╛рдВ рдХреБрдЫ рдЦрд╛рд╕ рдирд╣реАрдВ рд╣реИ: рдЪрд┐рддреНрд░ 320px рдЪреМрдбрд╝реЗ рд╣реИрдВ, рдЬреЛ рдПрдХ рджреВрд╕рд░реЗ рдХреЗ рдКрдкрд░ рдХреЗрдВрджреНрд░рд┐рдд рд╣реИрдВред

рд╣рдо рдЬреЗрдПрд╕ рдкрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВред рдореИрдВ рдкреВрд░рд╛ рдХреЛрдб рджреВрдВрдЧрд╛ред рд▓рд╛рдЗрдиреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╕реЗ рдбрд░реЛ рдордд, рд╕рдм рдХреБрдЫ рдХрд╛рдлреА рд╕рд░рд▓ рд╣реИ, рд╕рд┐рд╡рд╛рдп, рд╢рд╛рдпрдж, рдкрджреЛрдВ рдФрд░ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреА рдЧрдгрдирд╛ред рд▓рдЧрднрдЧ рд╣рд░ рдкрдВрдХреНрддрд┐ рдореЗрдВ рдПрдХ рдЯрд┐рдкреНрдкрдгреА рд╣реИред

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

рдЖрдкрдХреЗ рдзреНрдпрд╛рди рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред

All Articles