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 all 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
---

Change the [View Transition built-in animation](https://docs.astro.build/en/guides/view-transitions/#built-in-animation-directives) options.

The `transition:animate` value `morph` has been renamed to `initial`. Also, this is no longer the default animation.

If no `transition:animate` directive is specified, your animations will now default to `fade`.

Astro also supports a new `transition:animate` value, `none`. This value can be used on a page's `<html>` element to disable animated full-page transitions on an entire page.
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 @@ -77,7 +77,7 @@ export interface TransitionDirectionalAnimations {
backwards: TransitionAnimationPair;
}

export type TransitionAnimationValue = 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;
export type TransitionAnimationValue = '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 @@ -93,7 +93,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
112 changes: 56 additions & 56 deletions packages/astro/src/runtime/server/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type {
SSRResult,
TransitionAnimation,
TransitionAnimationValue,
TransitionDirectionalAnimations,
} from '../../@types/astro';
import { fade, slide } from '../../transitions/index.js';
import { markHTMLString } from './escape.js';
Expand All @@ -22,78 +21,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, '')
}

type Entries<T extends Record<string, any>> = Iterable<[keyof T, T[keyof T]]>

const getAnimations = (name: TransitionAnimationValue) => {
if (name === 'fade') return fade();
if (name === 'slide') return slide();
if (typeof name === 'object') return name;
}

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 `fade` (similar to `initial`, but snappier)
if (!animationName) animationName = 'fade';
const scope = createTransitionScope(result, hash);
const name = transitionName ? toValidIdent(transitionName) : scope;
const sheet = new ViewTransitionStyleSheet(scope, name);

const animations = getAnimations(animationName);
if (animations) {
for (const [direction, images] of Object.entries(animations) as Entries<typeof animations>) {
for (const [image, rules] of Object.entries(images) as Entries<typeof animations[typeof direction]>) {
sheet.addAnimationPair(direction, image, rules);
}
}
} else if (animationName === 'none') {
sheet.addAnimationRaw('old', 'animation: none; opacity: 0; mix-blend-mode: normal;')
sheet.addAnimationRaw('new', 'animation: none; mix-blend-mode: normal;')
}

const scope = createTransitionScope(result, hash);
result._metadata.extraHead.push(markHTMLString(`<style>${sheet.toString()}</style>`));
return scope;
}

// Default transition name is the scope of the element, ie HASH-1
if (!transitionName) {
transitionName = scope;
}
class ViewTransitionStyleSheet {
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved
private modern: string[] = []
private fallback: string[] = []

const styles = markHTMLString(`<style>[data-astro-transition-scope="${scope}"] {
view-transition-name: ${transitionName};
}
${
!animations
? ``
: // Regular animations
`
::view-transition-old(${transitionName}) {
${stringifyAnimation(animations.forwards.old)}
}
[data-astro-transition-fallback=old] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.forwards.old)}
}
constructor(private scope: string, private name: string) {}

::view-transition-new(${transitionName}) {
${stringifyAnimation(animations.forwards.new)}
}
[data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.forwards.new)}
}
toString() {
const { scope, name } = this;
const [modern, fallback] = [this.modern, this.fallback].map(rules => rules.join(''));
return [`[data-astro-transition-scope="${scope}"] { view-transition-name: ${name}; }`, this.layer(modern), fallback].join('')
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved
}

[data-astro-transition=back]::view-transition-old(${transitionName}) {
${stringifyAnimation(animations.backwards.old)}
}
[data-astro-transition=back][data-astro-transition-fallback=old] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.backwards.old)}
}
private layer(cssText: string) {
return cssText ? `@layer astro { ${cssText} }` : '';
}

[data-astro-transition=back]::view-transition-new(${transitionName}) {
${stringifyAnimation(animations.backwards.new)}
}
[data-astro-transition=back][data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.backwards.new)}
}
`.trim()
private addRule(target: 'modern' | 'fallback', cssText: string) {
this[target].push(cssText);
}
</style>`);

result._metadata.extraHead.push(styles);
addAnimationRaw(image: 'old' | 'new' | 'group', animation: string) {
const { scope, name } = this;
this.addRule('modern', `::view-transition-${image}(${name}) { ${animation} }`)
this.addRule('fallback', `[data-astro-transition-fallback="${image}"] [data-astro-transition-scope="${scope}"] { ${animation} }`)
}

return scope;
addAnimationPair(direction: 'forwards' | 'backwards', image: 'old' | 'new', rules: TransitionAnimation | TransitionAnimation[]) {
const { scope, name } = this;
const animation = stringifyAnimation(rules);
const prefix = direction === 'backwards' ? `[data-astro-transition=back]` : '';
this.addRule('modern', `${prefix}::view-transition-${image}(${name}) { ${animation} }`)
this.addRule('fallback', `${prefix}[data-astro-transition-fallback="${image}"] [data-astro-transition-scope="${scope}"] { ${animation} }`)
}
}

type AnimationBuilder = {
Expand Down Expand Up @@ -137,7 +138,6 @@ function stringifyAnimations(anims: TransitionAnimation[]): string {
const builder = animationBuilder();

for (const anim of anims) {
/*300ms cubic-bezier(0.4, 0, 0.2, 1) both astroSlideFromRight;*/
if (anim.duration) {
addAnimationProperty(builder, 'animation-duration', toTimeValue(anim.duration));
}
Expand Down
34 changes: 18 additions & 16 deletions packages/astro/src/transitions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { TransitionAnimationPair, TransitionDirectionalAnimations } from '../@types/astro';

const EASE_IN_OUT_QUART = 'cubic-bezier(0.76, 0, 0.24, 1)';

export function slide({
duration,
}: {
Expand All @@ -11,28 +13,28 @@ export function slide({
{
name: 'astroFadeOut',
duration: duration ?? '90ms',
easing: 'cubic-bezier(0.4, 0, 1, 1)',
easing: EASE_IN_OUT_QUART,
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_OUT_QUART,
delay: duration ? undefined : '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 @@ -45,22 +47,22 @@ export function slide({
}

export function fade({
duration,
duration
}: {
duration?: string | number;
} = {}): TransitionDirectionalAnimations {
const anim = {
old: {
name: 'astroFadeInOut',
duration: duration ?? '0.2s',
easing: 'linear',
fillMode: 'forwards',
name: 'astroFadeOut',
duration: duration ?? 180,
easing: EASE_IN_OUT_QUART,
fillMode: 'both',
},
new: {
name: 'astroFadeInOut',
duration: duration ?? '0.3s',
easing: 'linear',
fillMode: 'backwards',
name: 'astroFadeIn',
duration: duration ?? 180,
easing: EASE_IN_OUT_QUART,
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.