182 lines
4.2 KiB
JavaScript
182 lines
4.2 KiB
JavaScript
import { ref, computed } from 'vue'
|
|
|
|
// RAF 封装
|
|
const rafFn = (cb) => uni.requestAnimationFrame ? uni.requestAnimationFrame(cb) : setTimeout(cb, 16)
|
|
const cafFn = (id) => uni.cancelAnimationFrame ? uni.cancelAnimationFrame(id) : clearTimeout(id)
|
|
|
|
export function useSwipe() {
|
|
const bgOffsetX = ref(0)
|
|
const screenWidth = ref(375)
|
|
const tileWidth = ref(375)
|
|
|
|
let rawOffsetX = 0
|
|
let touchStartX = 0
|
|
let lastMoveX = 0
|
|
let lastMoveTime = 0
|
|
let velocity = 0
|
|
let inertiaRaf = null
|
|
let isInertiaPhase = false
|
|
let touchInBanner = false
|
|
|
|
let onTileChange = null
|
|
|
|
const cabinLayerStyle = computed(() => ({
|
|
transform: `translateX(${-tileWidth.value + bgOffsetX.value}px)`
|
|
}))
|
|
|
|
const backgroundStripStyle = computed(() => ({
|
|
width: `${tileWidth.value * 3}px`,
|
|
transform: `translateX(${-tileWidth.value + bgOffsetX.value}px)`
|
|
}))
|
|
|
|
const clampOffset = (offset) => {
|
|
const w = tileWidth.value
|
|
return (((offset % w) + w) % w) - w
|
|
}
|
|
|
|
const normalizeOffset = (offset) => {
|
|
const w = tileWidth.value
|
|
const normalized = clampOffset(offset)
|
|
const prevTileN = Math.floor(-rawOffsetX / w)
|
|
rawOffsetX += offset - bgOffsetX.value
|
|
const nextTileN = Math.floor(-rawOffsetX / w)
|
|
const delta = nextTileN - prevTileN
|
|
|
|
if (delta !== 0 && onTileChange) {
|
|
onTileChange(delta, isInertiaPhase)
|
|
}
|
|
|
|
return normalized
|
|
}
|
|
|
|
const stopInertia = () => {
|
|
if (inertiaRaf) {
|
|
cafFn(inertiaRaf)
|
|
inertiaRaf = null
|
|
}
|
|
isInertiaPhase = false
|
|
}
|
|
|
|
const scrollPage = (direction) => {
|
|
stopInertia()
|
|
|
|
if (onTileChange) {
|
|
onTileChange(direction, false)
|
|
}
|
|
|
|
const DURATION = 300
|
|
const FRAME = 16
|
|
const totalFrames = Math.round(DURATION / FRAME)
|
|
const totalDelta = tileWidth.value * direction * -1
|
|
let frame = 0
|
|
|
|
const step = () => {
|
|
frame++
|
|
const progress = frame / totalFrames
|
|
const eased = 1 - Math.pow(1 - progress, 3)
|
|
const prevEased = frame === 1 ? 0 : 1 - Math.pow(1 - (frame - 1) / totalFrames, 3)
|
|
const delta = totalDelta * (eased - prevEased)
|
|
|
|
bgOffsetX.value = clampOffset(bgOffsetX.value + delta)
|
|
rawOffsetX += delta
|
|
|
|
if (frame < totalFrames) {
|
|
inertiaRaf = rafFn(step)
|
|
}
|
|
}
|
|
|
|
inertiaRaf = rafFn(step)
|
|
}
|
|
|
|
const getBannerBottom = () => (screenWidth.value / 750) * 496
|
|
|
|
const onBgTouchStart = (e) => {
|
|
const touchY = e.touches[0].clientY
|
|
touchInBanner = touchY < getBannerBottom()
|
|
if (touchInBanner) return
|
|
|
|
stopInertia()
|
|
touchStartX = e.touches[0].clientX
|
|
lastMoveX = touchStartX
|
|
lastMoveTime = Date.now()
|
|
velocity = 0
|
|
}
|
|
|
|
const onBgTouchMove = (e) => {
|
|
if (touchInBanner) return
|
|
e.preventDefault()
|
|
|
|
const currentX = e.touches[0].clientX
|
|
const now = Date.now()
|
|
const dt = now - lastMoveTime || 1
|
|
velocity = (currentX - lastMoveX) / dt
|
|
lastMoveX = currentX
|
|
lastMoveTime = now
|
|
|
|
bgOffsetX.value = normalizeOffset(bgOffsetX.value + (currentX - touchStartX))
|
|
touchStartX = currentX
|
|
}
|
|
|
|
const onBgTouchEnd = () => {
|
|
if (touchInBanner) {
|
|
touchInBanner = false
|
|
return
|
|
}
|
|
touchInBanner = false
|
|
isInertiaPhase = true
|
|
|
|
const FRICTION = 0.8
|
|
const MIN_VELOCITY = 0.2
|
|
|
|
const step = () => {
|
|
velocity *= FRICTION
|
|
if (Math.abs(velocity) < MIN_VELOCITY) {
|
|
isInertiaPhase = false
|
|
return
|
|
}
|
|
bgOffsetX.value = normalizeOffset(bgOffsetX.value + velocity * 16)
|
|
inertiaRaf = rafFn(step)
|
|
}
|
|
|
|
inertiaRaf = rafFn(step)
|
|
}
|
|
|
|
const onBgTouchCancel = () => {
|
|
touchInBanner = false
|
|
stopInertia()
|
|
velocity = 0
|
|
}
|
|
|
|
const initSwipe = ({ screenW, tileW, onTileChangeCallback }) => {
|
|
screenWidth.value = screenW
|
|
tileWidth.value = tileW
|
|
onTileChange = onTileChangeCallback
|
|
bgOffsetX.value = 0
|
|
rawOffsetX = 0
|
|
velocity = 0
|
|
}
|
|
|
|
const reset = () => {
|
|
stopInertia()
|
|
bgOffsetX.value = 0
|
|
rawOffsetX = 0
|
|
velocity = 0
|
|
}
|
|
|
|
return {
|
|
bgOffsetX,
|
|
rawOffsetX,
|
|
velocity,
|
|
cabinLayerStyle,
|
|
backgroundStripStyle,
|
|
scrollPage,
|
|
stopInertia,
|
|
initSwipe,
|
|
reset,
|
|
onBgTouchStart,
|
|
onBgTouchMove,
|
|
onBgTouchEnd,
|
|
onBgTouchCancel,
|
|
}
|
|
}
|