feat(幕布): 一划到底改为整个幕布可见区生效

原先按距底 0.6 屏判断;改为由 InterselectionObserver 的幕布可见标志决定:滑到底后往上滑一点、幕布没离开视线时,再往下滑仍会滑到底

减弱动态效果下不接管滚动
This commit is contained in:
TermiNexus committed 2026-09-29 23:48:10 +08:00
1 parent fab527db6c
commit ede01786cb
1 file changed
+13 -5
+13 -5
View File
@@ -463,8 +463,7 @@ const CURTAIN_DASH_FALLBACK = 7.2
/** 幕布露出这么多比例即起笔 */
const CURTAIN_DRAW_AT = 0.6
/** 一划到底:距页面底部不足这么多屏时,下一次向下滑动直接滑到底 */
const CURTAIN_GLIDE_ZONE = 0.6
/** 一划到底:只要幕布还露在视口里(即 footer 之后那一段),再一次向下滑动就直接滑到底 */
const CURTAIN_GLIDE_MS = 1100
const curtainSvg = ref<SVGSVGElement | null>(null)
@@ -472,6 +471,10 @@ const curtainGroup = ref<SVGGElement | null>(null)
const curtainSpacer = ref<HTMLElement | null>(null)
const curtainDrawn = ref(false)
/** 幕布(末尾占位区)是否还在视口里。它就是"footer 之后"那段区域:
* 用户滑到底后往上滑一点、幕布没离开视线,此时再往下滑仍要一划到底 */
let curtainOnScreen = false
/** v-for 里的 <text> 引用,setupCurtain 要逐个量身定位 */
const letterEls: (SVGTextElement | null)[] = []
function setLetterEl(el: Element | ComponentPublicInstance | null, index: number): void {
@@ -527,6 +530,8 @@ function watchCurtain(): void {
curtainObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
// 幕布在不在视线里,决定"一划到底"生不生效
curtainOnScreen = entry.isIntersecting
if (entry.isIntersecting && entry.intersectionRatio >= CURTAIN_DRAW_AT) {
curtainDrawn.value = true
} else if (!entry.isIntersecting) {
@@ -539,14 +544,17 @@ function watchCurtain(): void {
curtainObserver.observe(spacer)
}
/** 一划到底:幕布已露头(距底不足 0.6 屏)时再一次向下滑动,就直接滑到底,
* 省得用户在最后半屏里一点点蹭;顺带让描边动画在整块幕布前完整播完 */
/** 一划到底:幕布还露在视口里时(footer 之后那一段,含滑到底后往上滑一点、幕布没离开视线的情况),
* 再一次向下滑动就直接滑到底,省得用户在最后半屏里一点点蹭;
* 顺带让描边动画在整块幕布前完整播完 */
function glideToBottom(): void {
if (prefersReducedMotion.matches) return // 减弱动态效果下不接管滚动
if (!curtainOnScreen) return // 幕布已离开视线(还在上面正文里),不插手
const now = performance.now()
if (now < curtainGlideUntil) return
const doc = document.documentElement
const remaining = doc.scrollHeight - window.scrollY - window.innerHeight
if (remaining <= 0 || remaining > window.innerHeight * CURTAIN_GLIDE_ZONE) return
if (remaining <= 0) return // 已经在最底,没什么可滑的
curtainGlideUntil = now + CURTAIN_GLIDE_MS + 300
const top = doc.scrollHeight
const lenis = lenisRef?.value