Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update built-in view transitions #8207

Merged
merged 22 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b9b4d83
feat: rename morph => initial
natemoo-re Aug 23, 2023
f074541
feat: update slide, fade animations, add none
natemoo-re Aug 23, 2023
0f00ee6
chore: add changeset
natemoo-re Aug 23, 2023
0d74e88
fix: bump compiler
natemoo-re Aug 23, 2023
ed94512
feat: disable root transition by default
natemoo-re Aug 23, 2023
74a4aea
chore: update changeset
natemoo-re Aug 23, 2023
4aecc46
chore: fix build
natemoo-re Aug 23, 2023
eb76640
Merge branch 'next' into feat/view-transition-animations
natemoo-re Aug 24, 2023
a05f746
feat(transitions): crossfade => fade
natemoo-re Aug 24, 2023
97535d3
feat(transitions): remove opinionated default
natemoo-re Aug 24, 2023
fc49600
chore: update changeset
natemoo-re Aug 24, 2023
398370d
feat(transitions): set root to fade
natemoo-re Aug 24, 2023
58c4c97
Merge branch 'next' into feat/view-transition-animations
natemoo-re Aug 24, 2023
e2ed940
Merge branch 'main' into feat/view-transition-animations
natemoo-re Aug 24, 2023
6b5d0f3
feat: remove opinionated root style
natemoo-re Aug 24, 2023
e5b8668
chore: remove unused easings
natemoo-re Aug 24, 2023
9d77180
feat: refactor transition logic, ensure defaults are wrapped in @layer
natemoo-re Aug 24, 2023
b84e5ef
Update .changeset/five-geese-crash.md
natemoo-re Aug 24, 2023
b0f411b
Update .changeset/five-geese-crash.md
natemoo-re Aug 24, 2023
ddc8625
Update .changeset/five-geese-crash.md
natemoo-re Aug 24, 2023
d64471a
Merge branch 'main' into feat/view-transition-animations
natemoo-re Aug 24, 2023
a7dd9b4
Update five-geese-crash.md
natemoo-re Aug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/five-geese-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'astro': major
---

Adjust default [View Transition](https://docs.astro.build/en/guides/view-transitions/) animations. Astro now sets opinionated defaults rather than relying on the browser's default User Agent styles.

Notably, Astro now disables the `root` animation rather than keeping the browser's default crossfade. The browser default behavior can still be used by setting the `transition:animate="initial"` directive on your `html` element.

Elements with a `transition:name` directive but no `transition:animate` directive will default to `crossfade`, a snappier version of the default User Agent animation.

Astro also supports a few new out-of-the-box `transition:animate` animations: `none` and `crossfade` join the existing `fade` and `slide` animations. `morph` has been renamed to `initial`.
13 changes: 13 additions & 0 deletions packages/astro/components/viewtransitions.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,16 @@
animation: none !important;
}
}

/* Opinionated Astro default: disable animation for root transition */
/* To opt-in to the default user agent style, set `<html transition:animate="initial">` */
::view-transition-image-pair(root) {
isolation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
display: block;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const { link } = Astro.props as Props;
</script>
</head>
<body>
<header transition:animate="morph">
<header transition:animate="initial">
<h1>testing</h1>
</header>
<main transition:animate="slide">
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
"@astrojs/compiler": "^2.0.0",
"@astrojs/compiler": "^2.0.1",
"@astrojs/internal-helpers": "workspace:*",
"@astrojs/markdown-remark": "workspace:*",
"@astrojs/telemetry": "workspace:*",
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export interface TransitionDirectionalAnimations {
backwards: TransitionAnimationPair;
}

export type TransitionAnimationValue = 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;
export type TransitionAnimationValue = 'crossfade' | 'initial' | 'slide' | 'fade' | 'none' | TransitionDirectionalAnimations;

// Allow users to extend this for astro-jsx.d.ts
// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand All @@ -94,7 +94,7 @@ export interface AstroBuiltinAttributes {
'set:html'?: any;
'set:text'?: any;
'is:raw'?: boolean;
'transition:animate'?: 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;
'transition:animate'?: TransitionAnimationValue;
'transition:name'?: string;
'transition:persist'?: boolean | string;
}
Expand Down
2 changes: 0 additions & 2 deletions packages/astro/src/core/compile/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ export async function compile({
astroGlobalArgs: JSON.stringify(astroConfig.site),
scopedStyleStrategy: astroConfig.scopedStyleStrategy,
resultScopedSlot: true,
experimentalTransitions: astroConfig.experimental.viewTransitions,
experimentalPersistence: astroConfig.experimental.viewTransitions,
transitionsAnimationURL: 'astro/components/viewtransitions.css',
preprocessStyle: createStylePreprocessor({
filename,
Expand Down
86 changes: 44 additions & 42 deletions packages/astro/src/runtime/server/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
TransitionAnimationValue,
TransitionDirectionalAnimations,
} from '../../@types/astro';
import { fade, slide } from '../../transitions/index.js';
import { fade, slide, crossfade } from '../../transitions/index.js';
import { markHTMLString } from './escape.js';

const transitionNameMap = new WeakMap<SSRResult, number>();
Expand All @@ -22,78 +22,80 @@ export function createTransitionScope(result: SSRResult, hash: string) {
return `astro-${hash}-${num}`;
}

// Ensure animationName is a valid CSS identifier
function toValidIdent(name: string): string {
return name.replace(/[^a-zA-Z0-9\-\_]/g, '_').replace(/^\_+|\_+$/g, '')
}

const BUILTIN_TRANSITION_ANIMATIONS = { fade, slide, crossfade };

export function renderTransition(
result: SSRResult,
hash: string,
animationName: TransitionAnimationValue | undefined,
transitionName: string
) {
let animations: TransitionDirectionalAnimations | null = null;
switch (animationName) {
case 'fade': {
animations = fade();
break;
}
case 'slide': {
animations = slide();
break;
}
default: {
if (typeof animationName === 'object') {
animations = animationName;
}
}
}

// Default to crossfade (similar to `initial`, but snappier)
if (!animationName) animationName = 'crossfade';
const scope = createTransitionScope(result, hash);

// Default transition name is the scope of the element, ie HASH-1
if (!transitionName) {
transitionName = scope;
const viewTransitionName = transitionName ? toValidIdent(transitionName) : scope;
const styles = [
`[data-astro-transition-scope="${scope}"] { view-transition-name: ${viewTransitionName}; }`,
]
if (animationName === 'fade' || animationName === 'slide' || animationName === 'crossfade') {
styles.push(generateAnimationStyle(scope, viewTransitionName, BUILTIN_TRANSITION_ANIMATIONS[animationName]()))
} else if (animationName === 'none') {
styles.push(generateAnimationNone(scope, viewTransitionName))
} else if (typeof animationName === 'object') {
styles.push(generateAnimationStyle(scope, viewTransitionName, animationName))
}
result._metadata.extraHead.push(markHTMLString(`<style>${styles.join('')}</style>`));

const styles = markHTMLString(`<style>[data-astro-transition-scope="${scope}"] {
view-transition-name: ${transitionName};
return scope;
}

function generateAnimationNone(scope: string, viewTransitionName: string) {
const oldSelectors = [
`::view-transition-old(${viewTransitionName})`,
`[data-astro-transition-fallback=old] [data-astro-transition-scope="${scope}"]`,
]
const newSelectors = [
`::view-transition-new(${viewTransitionName})`,
`[data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"]`,
]
return [
`${oldSelectors.join(', ')} { animation: none; opacity: 0; mix-blend-mode: normal; }`,
`${newSelectors.join(', ')} { animation: none; mix-blend-mode: normal; }`,
].join('')
}
${
!animations
? ``
: // Regular animations
`
::view-transition-old(${transitionName}) {

function generateAnimationStyle(scope: string, viewTransitionName: string, animations: TransitionDirectionalAnimations) {
return `::view-transition-old(${viewTransitionName}) {
${stringifyAnimation(animations.forwards.old)}
}
[data-astro-transition-fallback=old] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.forwards.old)}
}

::view-transition-new(${transitionName}) {
::view-transition-new(${viewTransitionName}) {
${stringifyAnimation(animations.forwards.new)}
}
[data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.forwards.new)}
}

[data-astro-transition=back]::view-transition-old(${transitionName}) {
[data-astro-transition=back]::view-transition-old(${viewTransitionName}) {
${stringifyAnimation(animations.backwards.old)}
}
[data-astro-transition=back][data-astro-transition-fallback=old] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.backwards.old)}
}

[data-astro-transition=back]::view-transition-new(${transitionName}) {
[data-astro-transition=back]::view-transition-new(${viewTransitionName}) {
${stringifyAnimation(animations.backwards.new)}
}
[data-astro-transition=back][data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.backwards.new)}
}
`.trim()
}
</style>`);

result._metadata.extraHead.push(styles);

return scope;
}`;
}

type AnimationBuilder = {
Expand Down
61 changes: 46 additions & 15 deletions packages/astro/src/transitions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { TransitionAnimationPair, TransitionDirectionalAnimations } from '../@types/astro';

const EASE_IN_QUART = 'cubic-bezier(0.5, 0, 0.75, 0)';
const EASE_OUT_QUART = 'cubic-bezier(0.25, 1, 0.5, 1)';
const EASE_IN_OUT_QUART = 'cubic-bezier(0.76, 0, 0.24, 1)';
const EASE_IN_OUT_QUINT = 'cubic-bezier(0.83, 0, 0.17, 1)';

export function slide({
duration,
}: {
Expand All @@ -11,28 +16,28 @@ export function slide({
{
name: 'astroFadeOut',
duration: duration ?? '90ms',
easing: 'cubic-bezier(0.4, 0, 1, 1)',
easing: EASE_OUT_QUART,
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved
fillMode: 'both',
},
{
name: 'astroSlideToLeft',
duration: duration ?? '300ms',
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
duration: duration ?? '220ms',
easing: EASE_IN_OUT_QUART,
fillMode: 'both',
},
],
new: [
{
name: 'astroFadeIn',
duration: duration ?? '210ms',
easing: 'cubic-bezier(0, 0, 0.2, 1)',
delay: '90ms',
easing: EASE_IN_QUART,
delay: '30ms',
fillMode: 'both',
},
{
name: 'astroSlideFromRight',
duration: duration ?? '300ms',
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
duration: duration ?? '220ms',
easing: EASE_IN_OUT_QUART,
fillMode: 'both',
},
],
Expand All @@ -51,16 +56,42 @@ export function fade({
} = {}): TransitionDirectionalAnimations {
const anim = {
old: {
name: 'astroFadeInOut',
duration: duration ?? '0.2s',
easing: 'linear',
fillMode: 'forwards',
name: 'astroFadeOut',
duration: duration ?? '150ms',
easing: EASE_OUT_QUART,
fillMode: 'both',
},
new: {
name: 'astroFadeIn',
duration: duration ?? '250ms',
easing: EASE_IN_QUART,
fillMode: 'both',
},
} satisfies TransitionAnimationPair;

return {
forwards: anim,
backwards: anim,
};
}

export function crossfade({
duration,
}: {
duration?: string | number;
} = {}): TransitionDirectionalAnimations {
const anim = {
old: {
name: 'astroFadeOut',
duration: duration ?? '180',
easing: EASE_IN_OUT_QUINT,
fillMode: 'both',
},
new: {
name: 'astroFadeInOut',
duration: duration ?? '0.3s',
easing: 'linear',
fillMode: 'backwards',
name: 'astroFadeIn',
duration: duration ?? '180',
easing: EASE_IN_OUT_QUINT,
fillMode: 'both',
},
} satisfies TransitionAnimationPair;

Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading