欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript實(shí)現(xiàn)手寫循環(huán)滑動(dòng)效果

 更新時(shí)間:2023年05月23日 14:42:33   作者:然燃  
最近一直在做業(yè)務(wù),遇到一個(gè)需求是頁面頂部需要展示圖片,可以拖動(dòng),拖動(dòng)到最后一張的時(shí)候需要無縫切換到第一張,從而實(shí)現(xiàn)循環(huán)滑動(dòng),所以本文就來和大家分享一下實(shí)現(xiàn)方法

最近一直在做業(yè)務(wù),遇到一個(gè)需求是: 頁面頂部需要展示圖片,可以拖動(dòng),拖動(dòng)到最后一張的時(shí)候需要無縫切換到第一張,可以實(shí)現(xiàn)循環(huán)滑動(dòng)。

這種需求場(chǎng)景挺常見的,比如淘寶,網(wǎng)易云,頁面頂部都是這個(gè)模式:

如果不要求循環(huán)滑動(dòng),css就能實(shí)現(xiàn):

父元素定寬,同時(shí)設(shè)置overflow-x:auto。子元素有寬度,橫向排列即可。

一旦要求循環(huán)滑動(dòng),或者不能用overflow,就會(huì)有點(diǎn)麻煩:

需要手動(dòng)實(shí)現(xiàn)滑動(dòng)的效果:

1.監(jiān)聽 touch 事件,監(jiān)聽touchStart,touchMove,touchEnd,計(jì)算位移

let start = 0, move = 0, end = 0
???????    const touchStart = (e) => {
        // 計(jì)算滑動(dòng)的起點(diǎn),同時(shí)需要保存上一次的停留的位置 
        start = e.touches[0].clientX - end
    }
    const touchMove = (e) => {
        // 記錄滑動(dòng)的距離
        move = e.touches[0].clientX - start
    }
    const touchEnd = () => {
        // 記錄結(jié)束滑動(dòng)的位置 
        end = move
    }

2.給父元素綁定位移,比如: transform: translateX(${transform}px)

<div className={cx('swapper')}
       onTouchStart={touchStart}
       onTouchMove={touchMove}
       onTouchEnd={touchEnd}
       style={{ transform: `translateX(${transform}px)`, transition: transition ? 'transform 0.3s' : 'none' }}
   >
       {
           list.map((item, index) => {
           return <div></div>
           })
        }
        </div>

當(dāng)滑動(dòng)到最后一張的時(shí)候,跳回第一張,實(shí)現(xiàn)首尾銜接:

1.將列表的第一張復(fù)制一份,放到最后一張后面。把最后一張復(fù)制一份,放到第一張前面

if (length > 1) {
    const startItem = list[0]
    const endItem = list[length - 1]
    list.push(startItem)
    list.unshift(endItem)
}

2.滑動(dòng)的時(shí)候設(shè)置彈性回彈,比如卡片滑動(dòng)了一半,需要就近回彈到最近的卡片計(jì)算下卡片的滑動(dòng)節(jié)點(diǎn):比如[-750,-375,0,375]

const length = list.length
    const boundary = []
    const width = document.body.clientWidth
    for (let i = -length; i <= length - 1; i++) {
        boundary.push(i * width)
    }

判斷下滑動(dòng)時(shí),距離左右卡片哪個(gè)最近:

for (let i = 1; i < boundary.length; i++) {
     // 最后卡片結(jié)束的位置在某個(gè)區(qū)間之內(nèi)
    if (end > boundary[i - 1] && end < boundary[i]) {
        // 通過減法判斷下距離左邊還是距離右邊更近
        if (end - boundary[i - 1] < boundary[i] - end) {
            end = boundary[i - 1]
            // 設(shè)置最終的位置
            setTransform(boundary[i - 1])
        } else {
            end = boundary[i]
            setTransform(boundary[i])
        }
        break
    }
}

回彈卡片的時(shí)候需要過渡效果,但滑動(dòng)卡片的時(shí)候不需要過渡效果,所以在touchMove階段移除transition,在touchEnd階段開啟過渡效果。

滑動(dòng)結(jié)束后,需要判斷邊界情況:是否是最后一張,需要跳轉(zhuǎn)到第一張。需要注意,這個(gè)時(shí)候是不需要過渡效果的:

if (end < boundary[0]) {
    end = boundary[0]
    setTransform(end)
}
if (end > boundary[boundary.length - 1]) {
    end = boundary[boundary.length - 1]
    setTransform(end)
}
setTimeout(() => {
    // 關(guān)閉過渡效果
    setTransition(false)
    // 如果是第二張,需要跳轉(zhuǎn)到倒數(shù)第二張
    if (end < boundary[1]) {
        end = boundary[boundary.length - 2]
        setTransform(end)
    }
    // 如果是倒數(shù)第二張,需要跳轉(zhuǎn)到第二張
    // 確?;瑒?dòng)的圖片都在中間,而不是在列表的第一張和最后一張
    if (end > boundary[boundary.length - 2]) {
        end = boundary[1]
        setTransform(end)
    }
    // 300 是因?yàn)樾枰却貜梽?dòng)畫結(jié)束
}, 300);

完整實(shí)現(xiàn)如下:

// 必須要放在組件外
let start = 0, move = 0, end = 0
const cx = classBind.bind(styles);
const Component = (props) => {
    const { arr = [] } = props 
    const [list, setList] = useState(arr)
    // 控制位移
    const [transform, setTransform] = useState(0)
    // 控制過渡效果
    const [transition, setTransition] = useState(false)
    // 記錄初始位置
    const touchStart = (e) => {
        start = e.touches[0].clientX - end
    }
    // 計(jì)算位移
    const touchMove = (e) => {
        move = e.touches[0].clientX - start
        setTransition(false)
        setTransform(move)
    }
    // 結(jié)束后計(jì)算回彈,和邊界情況
    const touchEnd = () => {
        setTransition(true)
        const length = list[0].listLength
        const boundary = []
        const width = document.body.clientWidth
        for (let i = -length; i <= length - 1; i++) {
            boundary.push(i * width)
        }
        end = move
        if (end < boundary[0]) {
            end = boundary[0]
            setTransform(end)
        }
        if (end > boundary[boundary.length - 1]) {
            end = boundary[boundary.length - 1]
            setTransform(end)
        }
        setTimeout(() => {
            setTransition(false)
            if (end < boundary[1]) {
                end = boundary[boundary.length - 2]
                setTransform(end)
            }
            if (end > boundary[boundary.length - 2]) {
                end = boundary[1]
                setTransform(end)
            }
        }, 300);
        for (let i = 1; i < boundary.length; i++) {
            if (end > boundary[i - 1] && end < boundary[i]) {
                if (end - boundary[i - 1] < boundary[i] - end) {
                    end = boundary[i - 1]
                    setTransform(boundary[i - 1])
                } else {
                    end = boundary[i]
                    setTransform(boundary[i])
                }
                break
            }
        }
    }
    useEffect(() => {
            const { length } = list
            list.forEach((item, index) => {
                // 保存真實(shí)的位置,因?yàn)楹竺鏁?huì)補(bǔ)充前后兩張卡片
                item.activeKey = index
                // 保存真實(shí)的列表長(zhǎng)度,不直接減2是因?yàn)橛?張卡片的情況
                item.listLength = length
            })
            if (length > 1) {
                const startItem = list[0]
                const endItem = list[length - 1]
                list.push(startItem)
                list.unshift(endItem)
            }
            setList([...list])
        })
    }, [arr]);
    return <div className={cx('resource-detail')}>
        <div className={cx('swapper')}
            onTouchStart={touchStart}
            onTouchMove={touchMove}
            onTouchEnd={touchEnd}
            style={{ transform: `translateX(${transform}px)`, transition: transition ? 'transform 0.3s' : 'none' }}
        >
            {
                list.map((item, index) => {
                    return <div key={index}>這是卡片</div>
                })
            }
        </div>
    </div>
}

到此這篇關(guān)于JavaScript實(shí)現(xiàn)手寫循環(huán)滑動(dòng)效果的文章就介紹到這了,更多相關(guān)JavaScript循環(huán)滑動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論