|
1 | 1 | import 'uno.css'; |
2 | | -import { type Router, useRouter, type Theme } from 'vitepress'; |
| 2 | +import { type Router, type Theme, useRouter } from 'vitepress'; |
3 | 3 | import DefaultTheme from 'vitepress/theme'; |
4 | 4 | import { |
5 | 5 | defineComponent, |
@@ -47,6 +47,34 @@ const checkAllImagesLoaded = () => { |
47 | 47 | return true; |
48 | 48 | }; |
49 | 49 |
|
| 50 | +const delay = (n = 0) => new Promise((r) => setTimeout(r, n)); |
| 51 | + |
| 52 | +let lastHashEl: HTMLElement | undefined = undefined; |
| 53 | +let newPage = true; |
| 54 | +const animateHashEl = async (hashEl?: HTMLElement) => { |
| 55 | + if (location.hash) { |
| 56 | + hashEl ??= document.querySelector<HTMLElement>(location.hash) || undefined; |
| 57 | + } |
| 58 | + if (!hashEl) return; |
| 59 | + const hintCls = 'animate-hash-hint'; |
| 60 | + if (lastHashEl) { |
| 61 | + lastHashEl.classList.remove(hintCls); |
| 62 | + } |
| 63 | + if (hashEl.classList.contains(hintCls)) { |
| 64 | + hashEl.classList.remove(hintCls); |
| 65 | + await delay(25); |
| 66 | + } |
| 67 | + hashEl.classList.add(hintCls); |
| 68 | + lastHashEl = hashEl; |
| 69 | + const ms = 500 * 2 * (newPage ? 5 : 2); |
| 70 | + newPage = false; |
| 71 | + await delay(ms); |
| 72 | + hashEl.classList.remove(hintCls); |
| 73 | + if (lastHashEl === hashEl) { |
| 74 | + lastHashEl = undefined; |
| 75 | + } |
| 76 | +}; |
| 77 | + |
50 | 78 | const handleCompatRedirect = async (router: Router) => { |
51 | 79 | // 兼容旧链接/短链重定向 |
52 | 80 | const u = location.href.substring(location.origin.length); |
@@ -95,39 +123,55 @@ const handleCompatRedirect = async (router: Router) => { |
95 | 123 | const hashEl = await (async () => { |
96 | 124 | const href = location.href; |
97 | 125 | let i = 0; |
98 | | - while (i < 20) { |
| 126 | + while (i < 25) { |
99 | 127 | if (href !== location.href) return; |
100 | 128 | const el = document.querySelector<HTMLElement>(location.hash); |
101 | 129 | if (el) { |
102 | 130 | return el; |
103 | 131 | } |
104 | 132 | i++; |
105 | | - await new Promise((r) => setTimeout(r, 250)); |
| 133 | + await delay(100); |
106 | 134 | } |
107 | 135 | })(); |
108 | 136 | if (hashEl) { |
109 | 137 | if (!isInViewport(hashEl)) { |
110 | | - // 图片加载完成会导致排版变化,此处手动判断后滚动到视口中 |
111 | | - // 也许后续可实现在构建时提前获取 size 后设置 aspect-ratio |
| 138 | + // 图片加载完成会导致排版变化, 即使提前使用相同比例的占位图也无法避免排版变化 |
112 | 139 | let i = 0; |
113 | 140 | while (i < 25 && !checkAllImagesLoaded()) { |
114 | 141 | await new Promise((r) => setTimeout(r, 100)); |
115 | 142 | i++; |
116 | 143 | } |
117 | | - hashEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
| 144 | + const anchor = document.querySelector( |
| 145 | + `a.header-anchor[href="${location.hash}"]`, |
| 146 | + ) as HTMLAnchorElement; |
| 147 | + if (anchor) { |
| 148 | + anchor.click(); |
| 149 | + } else { |
| 150 | + hashEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
| 151 | + } |
118 | 152 | } |
119 | | - const hintCls = 'animate-hash-hint'; |
120 | | - hashEl.classList.add(hintCls); |
121 | | - await new Promise((r) => setTimeout(r, 500 * 2 * 5)); |
122 | | - hashEl.classList.remove(hintCls); |
| 153 | + animateHashEl(hashEl); |
123 | 154 | } |
124 | 155 | } |
125 | 156 | }; |
126 | 157 |
|
| 158 | +if (!import.meta.env.SSR) { |
| 159 | + window.addEventListener('hashchange', async () => { |
| 160 | + animateHashEl(); |
| 161 | + }); |
| 162 | +} |
| 163 | + |
127 | 164 | const Redirect = defineComponent(() => { |
128 | 165 | const router = useRouter(); |
129 | 166 | onMounted(() => { |
130 | 167 | handleCompatRedirect(router); |
| 168 | + router.onAfterPageLoad = () => { |
| 169 | + console.log('page loaded'); |
| 170 | + nextTick().then(async () => { |
| 171 | + await delay(100); |
| 172 | + animateHashEl(); |
| 173 | + }); |
| 174 | + }; |
131 | 175 | }); |
132 | 176 | return () => {}; |
133 | 177 | }); |
|
0 commit comments