更新页面效果

This commit is contained in:
李琦
2026-07-31 11:36:19 +08:00
parent 3c4347128d
commit 4630035dd1
39 changed files with 859 additions and 54 deletions

View File

@@ -7,18 +7,18 @@
<span class="text-[12px] text-[#6b6863] font-light leading-relaxed mx-auto max-w-[280px] tracking-widest block whitespace-pre-wrap">{{ page.desc }}</span>
</div>
<!-- 单图满幅带留白卡纸 -->
<!-- 单图默认居中卡幅 / 可配置宽度铺满 -->
<div v-if="page.type === 'single'" v-reveal="{ variant: 'scale', delay: 100 }"
class="w-full max-w-[300px] mx-auto aspect-[4/5] mt-6">
<div :class="frameClass"><img :src="page.img1" /></div>
class="w-full mx-auto mt-6" :class="singleWrapClass" :style="singleWrapStyle">
<div :class="singleFrameClass"><img :src="page.img1" /></div>
</div>
<!-- 错落双图 -->
<div v-else-if="page.type === 'overlap'" class="relative w-full h-[50vh] mt-4">
<div v-reveal="{ variant: 'left', delay: 100 }" class="absolute top-0 right-4 w-[70%] h-[65%] z-10" :class="frameClass">
<!-- 错落双图高度 clamp 封顶图片贴容器边缘任何页高下不溢出 -->
<div v-else-if="page.type === 'overlap'" class="overlap-wrap relative w-full mt-2">
<div v-reveal="{ variant: 'left', delay: 100 }" class="absolute top-0 right-0 w-[68%] h-[72%] z-10" :class="frameClass">
<img :src="page.img1" />
</div>
<div v-reveal="{ variant: 'right', delay: 300 }" class="absolute bottom-4 left-4 w-[60%] h-[60%] z-20" :class="frameClass">
<div v-reveal="{ variant: 'right', delay: 300 }" class="absolute bottom-0 left-0 w-[60%] h-[60%] z-20" :class="frameClass">
<img :src="page.img2" />
</div>
</div>
@@ -89,6 +89,23 @@ const props = defineProps({
// 边框样式:前台按 page.borderStyle 套用全局 gf-* 类
const frameClass = computed(() => borderClass(props.page.borderStyle))
/* ---------- 单图宽度模式 ---------- */
// singleWidth: 'default'(居中卡幅) | 'full'(宽度铺满)singleKeepRatio: 铺满时是否保持原始宽高比
const singleWrapClass = computed(() =>
props.page.singleWidth === 'full' ? 'max-w-none' : 'max-w-[300px]'
)
const singleWrapStyle = computed(() => {
if (props.page.singleWidth === 'full') {
// 铺满 + 保持原始比例 → 高度由图片自然撑开;铺满 + 不保持 → 固定 4:5 裁切
return props.page.singleKeepRatio ? {} : { aspectRatio: '4 / 5' }
}
return { aspectRatio: '4 / 5' }
})
// 保持原始比例时 img 需要自然高度gf-natural 覆盖 gf-frame 的 height:100%
const singleFrameClass = computed(() =>
frameClass.value + (props.page.singleWidth === 'full' && props.page.singleKeepRatio ? ' gf-natural' : '')
)
/* ---------- 轮播状态 ---------- */
const index = ref(0)
const trackRef = ref(null)
@@ -120,6 +137,9 @@ function polaroidTransform(i) {
<style scoped>
.gallery { width: 100%; }
/* 错落双图:高度随视口缩放并封顶,保证标题+图片总高始终落在页高预算内 */
.overlap-wrap { height: clamp(320px, 54vh, 460px); }
/* 瀑布流 */
.g-masonry { column-count: 2; column-gap: 10px; }
.g-masonry-item { break-inside: avoid; margin-bottom: 10px; border-radius: 6px; }

View File

@@ -6,6 +6,11 @@ export const BORDER_STYLES = [
{ value: 'rounded', label: '柔圆角' },
{ value: 'double', label: '双线框' },
{ value: 'gold', label: '古典金框' },
{ value: 'shadow', label: '阴影浮起' },
{ value: 'ink', label: '墨黑描边' },
{ value: 'card', label: '悬浮卡片' },
{ value: 'lace', label: '虚线文艺' },
{ value: 'glow', label: '柔光金晕' },
]
export function borderClass(style) {

View File

@@ -26,44 +26,76 @@
}
/* ---------- 飘落花瓣(优雅:柔和渐变 + 轻微摇曳) ---------- */
/* 性能约定:摇曳动画一律作用在内层元素的 transform 上,
外层只跑位移动画 —— 全程合成层处理,不触发布局。 */
@keyframes petalFall {
0% { transform: translate3d(0, -12vh, 0) rotate(0deg); opacity: 0; }
10% { opacity: 0.85; }
90% { opacity: 0.7; }
10% { opacity: 0.8; }
90% { opacity: 0.65; }
100% { transform: translate3d(6vw, 112vh, 0) rotate(360deg); opacity: 0; }
}
@keyframes petalDrift {
0%, 100% { margin-left: -14px; }
50% { margin-left: 14px; }
0%, 100% { transform: translateX(-12px) rotate(-7deg); }
50% { transform: translateX(12px) rotate(7deg); }
}
.petal {
position: absolute;
top: 0;
pointer-events: none;
will-change: transform, opacity;
animation:
petalFall var(--p-dur, 12s) linear infinite,
petalDrift var(--p-sway, 6s) ease-in-out infinite alternate;
animation: petalFall var(--p-dur, 12s) linear infinite;
}
.petal-inner {
display: block;
width: 100%;
height: 100%;
will-change: transform;
animation: petalDrift var(--p-sway, 6s) ease-in-out infinite alternate;
}
/* ---------- 漂浮爱心(全屏自然上升,随机分布 ---------- */
/* ---------- 漂浮爱心(全屏自然上升,三层景深 ---------- */
@keyframes heartFloat {
0% { transform: translateY(20px) translateX(0) scale(0.7); opacity: 0; }
12% { opacity: 0.8; }
85% { opacity: 0.6; }
100% { transform: translateY(-118vh) translateX(24px) scale(1.1); opacity: 0; }
0% { transform: translate3d(0, 20px, 0) scale(0.7); opacity: 0; }
12% { opacity: var(--h-opacity, 0.7); }
85% { opacity: calc(var(--h-opacity, 0.7) * 0.75); }
100% { transform: translate3d(24px, -118vh, 0) scale(1.08); opacity: 0; }
}
@keyframes heartSway {
0%, 100% { margin-left: -16px; }
50% { margin-left: 16px; }
0%, 100% { transform: translateX(-14px) scale(0.97); }
50% { transform: translateX(14px) scale(1.03); }
}
.heart-bubble {
position: absolute;
will-change: transform, opacity;
animation:
heartFloat var(--float-duration, 18s) linear infinite,
heartSway var(--sway-duration, 5s) ease-in-out infinite alternate;
animation: heartFloat var(--float-duration, 18s) linear infinite;
}
.heart-inner {
display: inline-block;
will-change: transform;
animation: heartSway var(--sway-duration, 5s) ease-in-out infinite alternate;
}
/* ---------- 背景柔光层(淡金/淡粉光斑,缓慢漂移,不喧宾夺主) ---------- */
.bg-glow { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.bg-glow i { position: absolute; border-radius: 50%; filter: blur(46px); will-change: transform; }
.bg-glow .g1 {
width: 300px; height: 300px; top: -90px; right: -70px;
background: radial-gradient(circle, rgba(233, 206, 168, 0.5), transparent 68%);
animation: glowDrift1 17s ease-in-out infinite alternate;
}
.bg-glow .g2 {
width: 260px; height: 260px; bottom: -80px; left: -60px;
background: radial-gradient(circle, rgba(244, 194, 194, 0.42), transparent 68%);
animation: glowDrift2 21s ease-in-out infinite alternate;
}
.bg-glow .g3 {
width: 210px; height: 210px; top: 38%; left: 58%;
background: radial-gradient(circle, rgba(212, 175, 55, 0.16), transparent 70%);
animation: glowDrift3 24s ease-in-out infinite alternate;
}
@keyframes glowDrift1 { to { transform: translate(-34px, 26px) scale(1.08); } }
@keyframes glowDrift2 { to { transform: translate(30px, -24px) scale(1.06); } }
@keyframes glowDrift3 { to { transform: translate(-26px, 20px) scale(1.12); } }
/* ---------- 心跳脉冲 ---------- */
@keyframes pulseHeart {

View File

@@ -69,3 +69,57 @@
box-shadow: 0 0 0 4px #fff, 0 0 0 5px #e7d6bb, 0 8px 22px rgba(120, 90, 60, 0.12);
background: #fff;
}
/* 阴影浮起(无边框,大柔影悬浮感) */
.gf-shadow {
padding: 0;
border: 0;
border-radius: 10px;
box-shadow: 0 18px 44px rgba(96, 72, 50, 0.2), 0 4px 12px rgba(96, 72, 50, 0.1);
background: #fff;
}
/* 墨黑描边(简约深色细框) */
.gf-ink {
padding: 6px;
border: 1.5px solid #3a3733;
border-radius: 2px;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1);
background: #fff;
}
/* 悬浮卡片(厚白边 + 大圆角 + 层叠影) */
.gf-card {
padding: 12px;
border: 1px solid rgba(168, 140, 107, 0.18);
border-radius: 16px;
box-shadow: 0 2px 6px rgba(120, 90, 60, 0.08), 0 16px 36px rgba(120, 90, 60, 0.16);
background: #fff;
}
/* 虚线文艺dashed 描边 + 内白边) */
.gf-lace {
padding: 7px;
border: 1.5px dashed #b89a78;
border-radius: 8px;
box-shadow: 0 3px 12px rgba(120, 90, 60, 0.08);
background: #fff;
}
/* 柔光金晕(浅金边 + 外发光) */
.gf-glow {
padding: 5px;
border: 1px solid #e7d3ae;
border-radius: 6px;
box-shadow: 0 0 0 3px rgba(231, 211, 174, 0.35), 0 0 26px rgba(201, 169, 120, 0.45), 0 6px 16px rgba(120, 90, 60, 0.1);
background: #fff;
}
/* 辅助:图片保持原始宽高比(单图铺满+保比例时使用,覆盖 gf-frame 的 height:100% */
.gf-natural {
height: auto !important;
}
.gf-natural > img {
height: auto;
object-fit: contain;
}

View File

@@ -89,6 +89,22 @@
</a-radio-group>
<div class="text-[11px] text-[#8A8680] mt-1">整页模式会带来更有仪式感的逐屏切换效果</div>
</a-form-item>
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6">
<a-form-item label="自由滚动速度(滚动间隔)">
<div class="flex gap-3 items-center">
<a-slider v-model:value="cfg.freeScrollInterval" :min="10" :max="500" class="flex-1" />
<a-input-number v-model:value="cfg.freeScrollInterval" :min="10" :max="500" addon-after="ms" class="w-[120px]" />
</div>
<div class="text-[11px] text-[#8A8680] mt-1">间隔越小滚动越快默认 50ms仅在自由滚动模式下生效</div>
</a-form-item>
<a-form-item label="页面切换速度(动画时长)">
<div class="flex gap-3 items-center">
<a-slider v-model:value="cfg.pageSwitchDuration" :min="200" :max="3000" :step="100" class="flex-1" />
<a-input-number v-model:value="cfg.pageSwitchDuration" :min="200" :max="3000" :step="100" addon-after="ms" class="w-[120px]" />
</div>
<div class="text-[11px] text-[#8A8680] mt-1">整页/分屏模式下自动翻页的动画时长默认 800ms</div>
</a-form-item>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<a-form-item label="飘落花瓣">
<a-switch v-model:checked="cfg.petalEnabled" />
@@ -185,6 +201,19 @@
</div>
</div>
</div>
<!-- 单图版式的宽度设置逐页配置 -->
<div v-if="page.type==='single'" class="grid grid-cols-1 md:grid-cols-2 gap-x-6 items-start">
<a-form-item label="图片宽度">
<a-radio-group v-model:value="page.singleWidth">
<a-radio value="default">默认居中卡幅</a-radio>
<a-radio value="full">宽度铺满</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item v-if="page.singleWidth==='full'" label="保持原始宽高比">
<a-switch v-model:checked="page.singleKeepRatio" />
<div class="text-[11px] text-[#8A8680] mt-1">开启按图片原始比例显示关闭固定 4:5 比例裁切铺满</div>
</a-form-item>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6">
<a-form-item label="小标题 (英文)">
<a-input v-model:value="page.subtitle" />
@@ -295,6 +324,7 @@ function defaultConfig() {
musicList: [{ url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', name: '背景音乐 1' }],
activeMusicUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
scrollMode: 'snap', petalEnabled: true, bubbleEnabled: true, introEnabled: true,
freeScrollInterval: 50, pageSwitchDuration: 800,
photoPages: [
{ type: 'single', title: '初遇', subtitle: 'FIRST MET', desc: '世界那么大,还是遇见了你。\n于千万人之中没有早一步也没有晚一步。', img1: 'https://images.unsplash.com/photo-1606800052052-a08af7148866?auto=format&fit=crop&w=600&q=80', height: '100vh', borderStyle: 'mat' },
{ type: 'overlap', title: '相知', subtitle: 'FALL IN LOVE', desc: '纵然万劫不复,纵然相思入骨。\n我也待你眉眼如初岁月如故。', img1: 'https://images.unsplash.com/photo-1532712938310-34cb3982ef74?auto=format&fit=crop&w=600&q=80', img2: 'https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&w=600&q=80', height: '100vh', borderStyle: 'mat' },
@@ -354,6 +384,9 @@ function ensurePageShape(p) {
p.title = p.title || ''; p.subtitle = p.subtitle || ''; p.desc = p.desc || ''
p.height = p.height || '100vh'
p.borderStyle = p.borderStyle || 'mat'
// 单图宽度设置(仅 single 版式使用,逐页配置)
p.singleWidth = p.singleWidth === 'full' ? 'full' : 'default'
if (typeof p.singleKeepRatio !== 'boolean') p.singleKeepRatio = true
return p
}
@@ -482,6 +515,9 @@ onMounted(async () => {
}
Object.assign(cfg, loaded)
cfg.photoPages = (cfg.photoPages || []).map(ensurePageShape)
// 数值型参数兜底(兼容旧配置)
cfg.freeScrollInterval = Number(cfg.freeScrollInterval) || 50
cfg.pageSwitchDuration = Number(cfg.pageSwitchDuration) || 800
}
} catch (e) { /* 使用默认配置 */ }
if (adminStore.isAdmin) loadRsvp()

View File

@@ -19,22 +19,27 @@
</div>
</div>
<!-- 飘落花瓣层 -->
<!-- 背景柔光层淡金/淡粉光斑缓慢漂移 -->
<div class="bg-glow" aria-hidden="true"><i class="g1"></i><i class="g2"></i><i class="g3"></i></div>
<!-- 飘落花瓣层外层位移 + 内层摇曳 transform 不触发布局 -->
<div v-if="data.petalEnabled" class="absolute inset-0 pointer-events-none overflow-hidden z-[1]">
<div v-for="p in petals" :key="'p'+p.id" class="petal"
:style="{ left: p.left, width: p.size+'px', height: p.size+'px', opacity: p.opacity, animationDelay: p.delay, '--p-dur': p.duration, '--p-sway': p.sway }">
<svg viewBox="0 0 100 100" class="w-full h-full" :style="{ filter: 'drop-shadow(0 2px 3px rgba(150,110,90,0.12))' }">
:style="{ left: p.left, width: p.size+'px', height: p.size+'px', opacity: p.opacity, animationDelay: p.delay, '--p-dur': p.duration }">
<svg viewBox="0 0 100 100" class="petal-inner" :style="{ '--p-sway': p.sway, filter: p.filter }">
<path d="M50 3 C28 26 20 62 50 97 C80 62 72 26 50 3 Z" :fill="p.color" opacity="0.9" />
<path d="M50 9 C50 36 50 70 50 91" stroke="rgba(255,255,255,0.55)" stroke-width="1.4" fill="none" opacity="0.5" />
</svg>
</div>
</div>
<!-- 漂浮爱心层 -->
<!-- 漂浮爱心层三层景深远小淡模糊 / 近大清晰 -->
<div v-if="data.bubbleEnabled" class="absolute inset-0 pointer-events-none overflow-hidden z-[1]">
<div v-for="b in bubbles" :key="'b'+b.id"
class="heart-bubble flex items-center justify-center"
:style="{ left: b.left, top: b.top, color: b.color, fontSize: b.size+'px', opacity: b.opacity, animationDelay: b.delay, '--float-duration': b.floatDuration, '--sway-duration': b.swayDuration }"></div>
:style="{ left: b.left, top: b.top, color: b.color, fontSize: b.size+'px', animationDelay: b.delay, '--float-duration': b.floatDuration, '--h-opacity': b.opacity }">
<span class="heart-inner" :style="{ '--sway-duration': b.swayDuration, filter: b.filter }"></span>
</div>
</div>
<!-- 顶部滚动进度条 -->
@@ -72,9 +77,9 @@
</div>
</div>
<!-- 主滚动区域 -->
<!-- 主滚动区域注意不要加 scroll-smooth它会劫持 JS 滚动赋值导致卡顿 -->
<div ref="scrollContainerRef"
class="w-full h-full overflow-y-auto no-scrollbar relative scroll-smooth"
class="w-full h-full overflow-y-auto no-scrollbar relative"
:class="data.scrollMode === 'snap' ? 'snap-y snap-mandatory' : ''"
@scroll="handleScroll" @touchstart="handleUserInteraction" @touchmove="handleUserInteraction"
@wheel="handleUserInteraction" @mousedown="handleUserInteraction">
@@ -223,6 +228,8 @@ import PhotoGallery from '@/components/PhotoGallery.vue'
const DEFAULTS = {
scrollMode: 'snap', petalEnabled: true, bubbleEnabled: true, introEnabled: true,
musicList: [], activeMusicUrl: '',
freeScrollInterval: 50, // 自由滚动速度:滚动间隔 ms越小越快
pageSwitchDuration: 800, // 页面切换速度:翻页动画时长 ms
}
const data = ref({
@@ -247,22 +254,59 @@ const showIntro = ref(false), introOpening = ref(false), showTrackList = ref(fal
const rsvpForm = reactive({ name: '', guest_count: '1', wishes: '' })
const rsvpOptions = [ { value: '1', label: '1 人出席' }, { value: '2', label: '2 人出席' }, { value: '3', label: '3 人及以上' }, { value: '0', label: '遗憾缺席' } ]
let rafId = null, snapTimer = null, pauseTimer = null, introTimer = null
let rafId = null, snapTimer = null, pauseTimer = null, introTimer = null, freeTimer = null, animId = null
const loveColors = ['#a88c6b', '#e8a5b2', '#f4c2c2', '#d4af37']
const petalColors = ['#f7d9e0', '#fbeee3', '#f3c6d3', '#efd9c4', '#f9e7d6']
const bubbles = Array.from({ length: 15 }).map((_, i) => ({
id: i, left: Math.random() * 100 + '%', top: Math.random() * 100 + '%',
color: loveColors[i % 4], size: 13 + Math.random() * 10, opacity: 0.25 + Math.random() * 0.15,
floatDuration: (14 + Math.random() * 10) + 's', swayDuration: (4 + Math.random() * 2) + 's',
delay: '-' + (Math.random() * 18).toFixed(1) + 's',
}))
const petals = Array.from({ length: 18 }).map((_, i) => ({
id: i, left: Math.random() * 100 + '%', size: 10 + Math.random() * 14,
opacity: 0.4 + Math.random() * 0.4, color: petalColors[i % petalColors.length],
duration: (9 + Math.random() * 8) + 's', delay: (Math.random() * 10) + 's',
sway: (4 + Math.random() * 4) + 's',
}))
// 后台可配置的滚动参数(带默认值兜底,防止脏数据)
const freeInterval = computed(() => {
const v = Number(data.value.freeScrollInterval)
return v >= 10 && v <= 1000 ? v : 50
})
const switchDuration = computed(() => {
const v = Number(data.value.pageSwitchDuration)
return v >= 0 && v <= 5000 ? v : 800
})
// 装饰元素配色(柔化后的低饱和金粉系,与主题协调不抢眼)
const loveColors = ['#c9b08a', '#e2b3c0', '#d4af37', '#e8c9d2']
const petalColors = ['#f7dce3', '#fbeee3', '#f3c9d6', '#efdcc8', '#f9e7d6']
// 爱心三层景深:远(小/淡/模糊) 中 近(大/清晰),营造纵深而不杂乱
const heartTiers = [
{ n: 5, size: [10, 13], opacity: [0.1, 0.16], blur: 1.6, dur: [20, 26] }, // 远景
{ n: 4, size: [14, 17], opacity: [0.16, 0.24], blur: 0.7, dur: [16, 21] }, // 中景
{ n: 3, size: [18, 22], opacity: [0.24, 0.32], blur: 0, dur: [13, 17] }, // 近景
]
let heartId = 0
const bubbles = heartTiers.flatMap((t) =>
Array.from({ length: t.n }).map(() => {
const id = heartId++
return {
id,
left: Math.random() * 100 + '%', top: Math.random() * 100 + '%',
color: loveColors[id % loveColors.length],
size: t.size[0] + Math.random() * (t.size[1] - t.size[0]),
opacity: +(t.opacity[0] + Math.random() * (t.opacity[1] - t.opacity[0])).toFixed(2),
filter: t.blur ? `blur(${t.blur}px)` : 'none',
floatDuration: (t.dur[0] + Math.random() * (t.dur[1] - t.dur[0])).toFixed(1) + 's',
swayDuration: (4.5 + Math.random() * 2.5).toFixed(1) + 's',
delay: '-' + (Math.random() * 20).toFixed(1) + 's',
}
})
)
// 花瓣:两层景深,远处更小更淡带轻微模糊
const petals = Array.from({ length: 16 }).map((_, i) => {
const far = i % 3 === 0
return {
id: i, left: Math.random() * 100 + '%',
size: far ? 8 + Math.random() * 7 : 13 + Math.random() * 11,
opacity: far ? 0.3 + Math.random() * 0.15 : 0.42 + Math.random() * 0.3,
color: petalColors[i % petalColors.length],
filter: far ? 'blur(1.2px) drop-shadow(0 2px 3px rgba(150,110,90,0.08))' : 'drop-shadow(0 2px 3px rgba(150,110,90,0.12))',
duration: (far ? 13 : 9) + Math.random() * 8 + 's',
delay: (Math.random() * 10) + 's',
sway: (4 + Math.random() * 4) + 's',
}
})
const curtainBg = computed(() => ({ backgroundImage: `url(${data.value.heroImg})` }))
@@ -285,18 +329,37 @@ const handleScroll = () => {
const handleUserInteraction = () => {
isInteracting.value = true
// 用户接管时立即取消程序化的翻页动画,避免与人争抢滚动
cancelAnimationFrame(animId)
clearTimeout(pauseTimer)
pauseTimer = setTimeout(() => (isInteracting.value = false), 3000)
}
const sectionEls = () => Array.from(scrollContainerRef.value?.querySelectorAll('.page-section') || [])
// 带时长的平滑滚动easeInOutQuadduration 由后台 pageSwitchDuration 控制
const smoothScrollTo = (el, target, duration) => {
cancelAnimationFrame(animId)
const start = el.scrollTop, dist = target - start
if (!dist) return
if (duration <= 0) { el.scrollTop = target; return }
const t0 = performance.now()
const tick = (now) => {
const p = Math.min(1, (now - t0) / duration)
const e = p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2
el.scrollTop = start + dist * e
if (p < 1) animId = requestAnimationFrame(tick)
}
animId = requestAnimationFrame(tick)
}
const goNextSection = () => {
const el = scrollContainerRef.value
if (!el) return
const top = el.scrollTop
const next = sectionEls().find((e) => e.offsetTop > top + 12)
if (next) el.scrollTo({ top: next.offsetTop, behavior: 'smooth' })
else isAutoScroll.value = false
// 循环逻辑:滚到最后一屏后回到第一屏继续轮播
smoothScrollTo(el, next ? next.offsetTop : 0, switchDuration.value)
}
const startAutoScroll = () => {
@@ -305,15 +368,14 @@ const startAutoScroll = () => {
if (isAutoScroll.value && !isInteracting.value && !isDrawerOpen.value) goNextSection()
}, 4500)
} else {
const step = () => {
// 自由滚动setInterval 按后台配置的间隔匀速推进(间隔越小越快)
freeTimer = setInterval(() => {
const el = scrollContainerRef.value
if (el && isAutoScroll.value && !isInteracting.value && !isDrawerOpen.value) {
el.scrollTop += 0.8
if (el.scrollTop >= el.scrollHeight - el.clientHeight - 5) isAutoScroll.value = false
el.scrollTop += 1.2
if (el.scrollTop >= el.scrollHeight - el.clientHeight - 5) el.scrollTop = 0
}
rafId = requestAnimationFrame(step)
}
step()
}, freeInterval.value)
}
}
@@ -378,7 +440,9 @@ onMounted(async () => {
onUnmounted(() => {
cancelAnimationFrame(rafId)
cancelAnimationFrame(animId)
clearInterval(snapTimer)
clearInterval(freeTimer)
clearTimeout(pauseTimer)
clearTimeout(introTimer)
})