编写复杂但有趣的JavaScript滑块



朋友们,美好的一天!我决定返回到滑块主题。本文作为启示有关带内置滑块的图库生成器的文章,请参见此处

我们将要写的滑块是基于无休止的卡片组或图像堆栈的原理工作的。顶部图像沿三个方向之一(左,右或上)被丢弃。丢弃的图像将替换为以下图像,依此类推直至无穷大。您可以上传图像或使用默认图像。

为了注册手势(触摸)和移动(拖放)使用Hammer.js该库可让您检测鼠标单击和手指触摸。

所以走吧

标记如下所示:

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

这里没什么特别的:图像宽320像素,彼此上下居中。

我们传递给JS。我将给出整个代码。不要害怕行数,除了可能计算位置和坐标之外,一切都非常简单。几乎每一行都有注释。

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