mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-14 01:53:55 +00:00
feat(nuxt): allow controlling view transitions in page meta (#25264)
This commit is contained in:
parent
d2cca0ebc8
commit
995acd9983
@ -426,6 +426,35 @@ export default defineNuxtConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The possible values are: `false`, `true`, or `'always'`.
|
||||||
|
|
||||||
|
If set to true, Nuxt will not apply transitions if the user's browser matches `prefers-reduced-motion: reduce` (recommended). If set to `always`, Nuxt will always apply the transition and it is up to you to respect the user's preference.
|
||||||
|
|
||||||
|
By default, view transitions are enabled for all [pages](/docs/guide/directory-structure/pages), but you can set a different global default.
|
||||||
|
|
||||||
|
```ts [nuxt.config.ts]
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
app: {
|
||||||
|
// Disable view transitions globally, and opt-in on a per page basis
|
||||||
|
viewTransition: false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to override the default `viewTransition` value for a page by setting the `viewTransition` key in [`definePageMeta`](/docs/api/utils/define-page-meta) of the page:
|
||||||
|
|
||||||
|
```vue [pages/about.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
viewTransition: false
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::alert{type="warning"}
|
||||||
|
Overriding view transitions on a per-page basis will only have an effect if you have enabled the `experimental.viewTransition` option.
|
||||||
|
::
|
||||||
|
|
||||||
If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to _disable_ Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents:
|
If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to _disable_ Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -33,6 +33,7 @@ interface PageMeta {
|
|||||||
alias?: string | string[]
|
alias?: string | string[]
|
||||||
pageTransition?: boolean | TransitionProps
|
pageTransition?: boolean | TransitionProps
|
||||||
layoutTransition?: boolean | TransitionProps
|
layoutTransition?: boolean | TransitionProps
|
||||||
|
viewTransition?: boolean | 'always'
|
||||||
key?: false | string | ((route: RouteLocationNormalizedLoaded) => string)
|
key?: false | string | ((route: RouteLocationNormalizedLoaded) => string)
|
||||||
keepalive?: boolean | KeepAliveProps
|
keepalive?: boolean | KeepAliveProps
|
||||||
layout?: false | LayoutKey | Ref<LayoutKey> | ComputedRef<LayoutKey>
|
layout?: false | LayoutKey | Ref<LayoutKey> | ComputedRef<LayoutKey>
|
||||||
@ -104,6 +105,14 @@ interface PageMeta {
|
|||||||
|
|
||||||
Set name of the transition to apply for current page. You can also set this value to `false` to disable the page transition.
|
Set name of the transition to apply for current page. You can also set this value to `false` to disable the page transition.
|
||||||
|
|
||||||
|
**`viewTransition`**
|
||||||
|
|
||||||
|
- **Type**: `boolean | 'always'`
|
||||||
|
|
||||||
|
**Experimental feature, only available when [enabled in your nuxt.config file](/docs/getting-started/transitions#view-transitions-api-experimental)**</br>
|
||||||
|
Enable/disable View Transitions for the current page.
|
||||||
|
If set to true, Nuxt will not apply the transition if the users browser matches `prefers-reduced-motion: reduce` (recommended). If set to `always`, Nuxt will always apply the transition.
|
||||||
|
|
||||||
**`redirect`**
|
**`redirect`**
|
||||||
|
|
||||||
- **Type**: [`RouteRecordRedirectOption`](https://router.vuejs.org/guide/essentials/redirect-and-alias.html#redirect-and-alias)
|
- **Type**: [`RouteRecordRedirectOption`](https://router.vuejs.org/guide/essentials/redirect-and-alias.html#redirect-and-alias)
|
||||||
|
@ -2,10 +2,12 @@ import { isChangingPage } from '../components/utils'
|
|||||||
import { useRouter } from '../composables/router'
|
import { useRouter } from '../composables/router'
|
||||||
import { defineNuxtPlugin } from '../nuxt'
|
import { defineNuxtPlugin } from '../nuxt'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { viewTransition } from '#build/nuxt.config.mjs'
|
import { appViewTransition as defaultViewTransition } from '#build/nuxt.config.mjs'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
if (!document.startViewTransition || (viewTransition !== 'always' && window.matchMedia('(prefers-reduced-motion: reduce)').matches)) { return }
|
if (!document.startViewTransition) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let finishTransition: undefined | (() => void)
|
let finishTransition: undefined | (() => void)
|
||||||
let abortTransition: undefined | (() => void)
|
let abortTransition: undefined | (() => void)
|
||||||
@ -13,9 +15,14 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
router.beforeResolve((to, from) => {
|
router.beforeResolve((to, from) => {
|
||||||
if (!isChangingPage(to, from)) {
|
const viewTransitionMode = to.meta.viewTransition ?? defaultViewTransition
|
||||||
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||||
|
const prefersNoTransition = prefersReducedMotion && viewTransitionMode !== 'always'
|
||||||
|
|
||||||
|
if (viewTransitionMode === false || prefersNoTransition || !isChangingPage(to, from)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise = new Promise<void>((resolve, reject) => {
|
const promise = new Promise<void>((resolve, reject) => {
|
||||||
finishTransition = resolve
|
finishTransition = resolve
|
||||||
abortTransition = reject
|
abortTransition = reject
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { existsSync, readdirSync } from 'node:fs'
|
import { existsSync, readdirSync } from 'node:fs'
|
||||||
import { mkdir, readFile } from 'node:fs/promises'
|
import { mkdir, readFile } from 'node:fs/promises'
|
||||||
import { addBuildPlugin, addComponent, addPlugin, addTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, findPath, logger, updateTemplates, useNitro } from '@nuxt/kit'
|
import { addBuildPlugin, addComponent, addPlugin, addTemplate, addTypeTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, findPath, logger, updateTemplates, useNitro } from '@nuxt/kit'
|
||||||
import { dirname, join, relative, resolve } from 'pathe'
|
import { dirname, join, relative, resolve } from 'pathe'
|
||||||
import { genImport, genObjectFromRawEntries, genString } from 'knitwork'
|
import { genImport, genObjectFromRawEntries, genString } from 'knitwork'
|
||||||
import { joinURL } from 'ufo'
|
import { joinURL } from 'ufo'
|
||||||
@ -473,6 +473,26 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// add page meta types if enabled
|
||||||
|
if (nuxt.options.experimental.viewTransition) {
|
||||||
|
addTypeTemplate({
|
||||||
|
filename: 'types/view-transitions.d.ts',
|
||||||
|
getContents: ({ nuxt }) => {
|
||||||
|
const runtimeDir = resolve(distDir, 'pages/runtime')
|
||||||
|
const composablesFile = relative(join(nuxt.options.buildDir, 'types'), resolve(runtimeDir, 'composables'))
|
||||||
|
return [
|
||||||
|
'import { ComputedRef, MaybeRef } from \'vue\'',
|
||||||
|
`declare module ${genString(composablesFile)} {`,
|
||||||
|
' interface PageMeta {',
|
||||||
|
` viewTransition?: boolean | 'always'`,
|
||||||
|
' }',
|
||||||
|
'}',
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Add <NuxtPage>
|
// Add <NuxtPage>
|
||||||
addComponent({
|
addComponent({
|
||||||
name: 'NuxtPage',
|
name: 'NuxtPage',
|
||||||
|
@ -145,6 +145,20 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
pageTransition: false,
|
pageTransition: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default values for view transitions.
|
||||||
|
*
|
||||||
|
* This only has an effect when **experimental** support for View Transitions is
|
||||||
|
* [enabled in your nuxt.config file](/docs/getting-started/transitions#view-transitions-api-experimental).
|
||||||
|
*
|
||||||
|
* This can be overridden with `definePageMeta` on an individual page.
|
||||||
|
* @see https://nuxt.com/docs/getting-started/transitions#view-transitions-api-experimental
|
||||||
|
* @type {typeof import('../src/types/config').NuxtAppConfig['viewTransition']}
|
||||||
|
*/
|
||||||
|
viewTransition: {
|
||||||
|
$resolve: async (val, get) => val ?? await get('experimental.viewTransition') ?? false
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default values for KeepAlive configuration between pages.
|
* Default values for KeepAlive configuration between pages.
|
||||||
*
|
*
|
||||||
|
@ -144,6 +144,7 @@ export interface NuxtAppConfig {
|
|||||||
head: AppHeadMetaObject
|
head: AppHeadMetaObject
|
||||||
layoutTransition: boolean | TransitionProps
|
layoutTransition: boolean | TransitionProps
|
||||||
pageTransition: boolean | TransitionProps
|
pageTransition: boolean | TransitionProps
|
||||||
|
viewTransition?: boolean | 'always'
|
||||||
keepalive: boolean | KeepAliveProps
|
keepalive: boolean | KeepAliveProps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user