55 lines
1.5 KiB
JavaScript
55 lines
1.5 KiB
JavaScript
/**
|
|
* 공개 라우트 이동 후 새 화면 기준으로 스크롤 위치를 정리한다.
|
|
*
|
|
* Vue Router 기본 scrollBehavior는 전환 중 이전 화면을 먼저 움직일 수 있어
|
|
* `app/router.options.js`에서 끄고, 여기서는 라우트가 확정된 뒤 프레임을
|
|
* 기다렸다가 새 화면에서만 최상단 또는 해시 위치로 이동한다.
|
|
*/
|
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
const router = nuxtApp.$router
|
|
|
|
/**
|
|
* 브라우저 렌더 프레임을 기다린다.
|
|
* @returns {Promise<void>} 프레임 대기 Promise
|
|
*/
|
|
const waitForFrame = () => new Promise((resolve) => {
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(resolve)
|
|
})
|
|
})
|
|
|
|
/**
|
|
* 이동 완료 후 스크롤 위치를 적용한다.
|
|
* @param {import('vue-router').RouteLocationNormalizedLoaded} to - 이동 대상 라우트
|
|
* @param {import('vue-router').RouteLocationNormalizedLoaded} from - 이전 라우트
|
|
* @returns {Promise<void>}
|
|
*/
|
|
const applyRouteScroll = async (to, from) => {
|
|
if (to.fullPath === from.fullPath) {
|
|
return
|
|
}
|
|
|
|
await waitForFrame()
|
|
|
|
if (to.hash) {
|
|
const targetId = decodeURIComponent(to.hash.slice(1))
|
|
const target = document.getElementById(targetId)
|
|
|
|
if (target) {
|
|
target.scrollIntoView({ block: 'start' })
|
|
return
|
|
}
|
|
}
|
|
|
|
window.scrollTo({
|
|
top: 0,
|
|
left: 0,
|
|
behavior: 'auto'
|
|
})
|
|
}
|
|
|
|
router.afterEach((to, from) => {
|
|
applyRouteScroll(to, from)
|
|
})
|
|
})
|