初始化
This commit is contained in:
107
src/components/FullPage.vue
Normal file
107
src/components/FullPage.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="fullpage-container" @wheel.prevent="handleWheel" @touchstart="handleTouchStart"
|
||||
@touchend="handleTouchEnd">
|
||||
<div class="fullpage-wrapper" :style="wrapperStyle">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="fullpage-dots">
|
||||
<span v-for="i in pageCount" :key="i" class="dot" :class="{ active: currentIndex === i - 1 }"
|
||||
@click="goTo(i - 1)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, useSlots } from 'vue'
|
||||
|
||||
const slots = useSlots()
|
||||
const pageCount = computed(() => {
|
||||
const defaultSlot = slots.default?.()
|
||||
return defaultSlot ? defaultSlot.length : 0
|
||||
})
|
||||
|
||||
const currentIndex = ref(0)
|
||||
const isAnimating = ref(false)
|
||||
const touchStartY = ref(0)
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
transform: `translateY(-${currentIndex.value * 100}vh)`,
|
||||
transition: isAnimating.value ? 'transform 0.8s cubic-bezier(0.65, 0, 0.35, 1)' : 'none',
|
||||
}))
|
||||
|
||||
function goTo(index) {
|
||||
if (isAnimating.value || index === currentIndex.value) return
|
||||
if (index < 0 || index >= pageCount.value) return
|
||||
isAnimating.value = true
|
||||
currentIndex.value = index
|
||||
setTimeout(() => {
|
||||
isAnimating.value = false
|
||||
}, 800)
|
||||
}
|
||||
|
||||
function handleWheel(e) {
|
||||
if (isAnimating.value) return
|
||||
if (e.deltaY > 0) {
|
||||
goTo(currentIndex.value + 1)
|
||||
} else if (e.deltaY < 0) {
|
||||
goTo(currentIndex.value - 1)
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchStart(e) {
|
||||
touchStartY.value = e.touches[0].clientY
|
||||
}
|
||||
|
||||
function handleTouchEnd(e) {
|
||||
const deltaY = touchStartY.value - e.changedTouches[0].clientY
|
||||
if (Math.abs(deltaY) > 50) {
|
||||
if (deltaY > 0) {
|
||||
goTo(currentIndex.value + 1)
|
||||
} else {
|
||||
goTo(currentIndex.value - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ currentIndex, goTo })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fullpage-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fullpage-wrapper {
|
||||
width: 100%;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.fullpage-dots {
|
||||
position: fixed;
|
||||
right: 30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dot.active {
|
||||
background: #fff;
|
||||
transform: scale(1.3);
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user