mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +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:
|
||||
|
||||
```js
|
||||
|
@ -33,6 +33,7 @@ interface PageMeta {
|
||||
alias?: string | string[]
|
||||
pageTransition?: boolean | TransitionProps
|
||||
layoutTransition?: boolean | TransitionProps
|
||||
viewTransition?: boolean | 'always'
|
||||
key?: false | string | ((route: RouteLocationNormalizedLoaded) => string)
|
||||
keepalive?: boolean | KeepAliveProps
|
||||
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.
|
||||
|
||||
**`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`**
|
||||
|
||||
- **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 { defineNuxtPlugin } from '../nuxt'
|
||||
// @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) => {
|
||||
if (!document.startViewTransition || (viewTransition !== 'always' && window.matchMedia('(prefers-reduced-motion: reduce)').matches)) { return }
|
||||
if (!document.startViewTransition) {
|
||||
return
|
||||
}
|
||||
|
||||
let finishTransition: undefined | (() => void)
|
||||
let abortTransition: undefined | (() => void)
|
||||
@ -13,9 +15,14 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
const router = useRouter()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
const promise = new Promise<void>((resolve, reject) => {
|
||||
finishTransition = resolve
|
||||
abortTransition = reject
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { existsSync, readdirSync } from 'node:fs'
|
||||
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 { genImport, genObjectFromRawEntries, genString } from 'knitwork'
|
||||
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>
|
||||
addComponent({
|
||||
name: 'NuxtPage',
|
||||
|
@ -145,6 +145,20 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -144,6 +144,7 @@ export interface NuxtAppConfig {
|
||||
head: AppHeadMetaObject
|
||||
layoutTransition: boolean | TransitionProps
|
||||
pageTransition: boolean | TransitionProps
|
||||
viewTransition?: boolean | 'always'
|
||||
keepalive: boolean | KeepAliveProps
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user