[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: only apply view transition between search <-> index pages (#1222)

authored by

Daniel Roe and committed by
GitHub
5f1fb789 eeeb6b61

+91 -1
+91
app/plugins/view-transitions.client.ts
··· 1 + /** 2 + * Scoped View Transitions plugin. 3 + * 4 + * Only triggers the View Transition API when navigating between `/` and `/search` 5 + * (the search-box morph animation). All other navigations are left untouched so 6 + * they feel instant. 7 + */ 8 + export default defineNuxtPlugin(nuxtApp => { 9 + if (!document.startViewTransition) return 10 + 11 + let transition: ViewTransition | undefined 12 + let finishTransition: (() => void) | undefined 13 + let hasUAVisualTransition = false 14 + 15 + const resetTransitionState = () => { 16 + transition = undefined 17 + finishTransition = undefined 18 + hasUAVisualTransition = false 19 + } 20 + 21 + // Respect browser-initiated visual transitions (e.g. swipe-back) 22 + window.addEventListener('popstate', event => { 23 + hasUAVisualTransition = 24 + (event as PopStateEvent & { hasUAVisualTransition?: boolean }).hasUAVisualTransition ?? false 25 + if (hasUAVisualTransition) { 26 + transition?.skipTransition() 27 + } 28 + }) 29 + 30 + const router = useRouter() 31 + 32 + router.beforeResolve(async (to, from) => { 33 + if (to.matched.length === 0) return 34 + 35 + const toPath = to.path 36 + const fromPath = from.path 37 + 38 + // Only transition between / and /search 39 + if (!isSearchTransition(toPath, fromPath)) return 40 + 41 + // Respect prefers-reduced-motion 42 + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return 43 + 44 + // Skip if browser already handled the visual transition 45 + if (hasUAVisualTransition) return 46 + 47 + const promise = new Promise<void>(resolve => { 48 + finishTransition = resolve 49 + }) 50 + 51 + let changeRoute: () => void 52 + const ready = new Promise<void>(resolve => (changeRoute = resolve)) 53 + 54 + transition = document.startViewTransition(() => { 55 + changeRoute!() 56 + return promise 57 + }) 58 + 59 + transition.finished.then(resetTransitionState) 60 + 61 + await nuxtApp.callHook('page:view-transition:start', transition) 62 + 63 + return ready 64 + }) 65 + 66 + // Abort on errors 67 + router.onError(() => { 68 + finishTransition?.() 69 + resetTransitionState() 70 + }) 71 + nuxtApp.hook('app:error', () => { 72 + finishTransition?.() 73 + resetTransitionState() 74 + }) 75 + nuxtApp.hook('vue:error', () => { 76 + finishTransition?.() 77 + resetTransitionState() 78 + }) 79 + 80 + // Finish when page render completes 81 + nuxtApp.hook('page:finish', () => { 82 + finishTransition?.() 83 + resetTransitionState() 84 + }) 85 + }) 86 + 87 + /** Return true when navigating between `/` and `/search` (either direction). */ 88 + function isSearchTransition(toPath: string, fromPath: string): boolean { 89 + const paths = new Set([toPath, fromPath]) 90 + return paths.has('/') && paths.has('/search') 91 + }
-1
nuxt.config.ts
··· 128 128 entryImportMap: false, 129 129 typescriptPlugin: true, 130 130 viteEnvironmentApi: true, 131 - viewTransition: true, 132 131 typedPages: true, 133 132 }, 134 133