Skip to content

Commit 8bee8ac

Browse files
committed
Sanding the edges of animations
1 parent e07a615 commit 8bee8ac

File tree

8 files changed

+193
-136
lines changed

8 files changed

+193
-136
lines changed

animations/AnimationStyle.astro

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
import { getKeyframes, getStyles } from './animation-style';
33
export interface Props {
44
name: string;
5+
keyframeName?: string;
56
}
6-
const { name = '' } = Astro.props;
7-
const css = getKeyframes(name) + getStyles(name);
7+
const { name = '', keyframeName } = Astro.props;
8+
const css = (getKeyframes(keyframeName ?? name) ?? '') + getStyles(name);
89
---
910

1011
<style set:text={css}></style>

animations/AnimationStyle.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default function AnimationStyle(_props: import("./AnimationStyle.astro").Props): any;

animations/CreateAnimationScope.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default function AnimationStyle(_props: import("./CreateAnimationScope.astro").Props): any;

animations/animation-style.ts

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ export type ScopeAndStyles = {
1111
scope: string;
1212
styles: string;
1313
};
14+
export type StyleSheetOptions = {
15+
transitionName: string,
16+
animations: NamedAnimationPairs,
17+
scope?: string,
18+
};
1419

15-
export const styleSheet = (transitionName: string, animations: NamedAnimationPairs): ScopeAndStyles => maybeScopedStyleSheet(transitionName, undefined, animations);
16-
17-
export const scopedStyleSheet = (transitionName: string, scope: string, animations: NamedAnimationPairs) => maybeScopedStyleSheet(transitionName, scope, animations).styles;
20+
export function styleSheet(options: StyleSheetOptions): ScopeAndStyles {
21+
const scope = options.scope ?? 'astro-' + Math.random().toString(36).slice(2, 8);
22+
const { transitionName, animations } = options;
1823

19-
export function maybeScopedStyleSheet(transitionName: string, scope: string | undefined, animations: NamedAnimationPairs): ScopeAndStyles {
20-
scope ??= 'astro-' + Math.random().toString(36).slice(2, 8);
2124
const header = `[data-astro-transition-scope=${scope}] {view-transition-name: ${transitionName};}@layer astro {`;
2225
const closeLayer = '}';
2326

@@ -62,40 +65,43 @@ map["fillMode"] = "animation-fill-mode";
6265
map["direction"] = "animation-direction";
6366
const timeString = (value: number | string) => typeof value === 'number' ? value + 'ms' : value;
6467

65-
export const extend = (base: TransitionDirectionalAnimations, extension?: NamedAnimationPairs) => ({
66-
forwards: {
67-
old: Object.fromEntries([
68-
...Object.entries(base.forwards.old).map(([key, value]) => [map[key], timeString(value)]),
69-
...Object.entries(extension?.forwards?.old ?? {}),
70-
]),
71-
new: Object.fromEntries([
72-
...Object.entries(base.forwards.new).map(([key, value]) => [map[key], timeString(value)]),
73-
...Object.entries(extension?.forwards?.new ?? {}),
74-
]),
75-
},
76-
backwards: {
77-
old: Object.fromEntries([
78-
...Object.entries(base.backwards.old).map(([key, value]) => [map[key], value]),
79-
...Object.entries(extension?.backwards?.old ?? {}),
80-
]),
81-
new: Object.fromEntries([
82-
...Object.entries(base.backwards.new).map(([key, value]) => [map[key], value]),
83-
...Object.entries(extension?.backwards?.new ?? {}),
84-
]),
85-
},
86-
});
68+
export const extend = (base: TransitionDirectionalAnimations,
69+
extension?: NamedAnimationPairs) => {
70+
if (
71+
Array.isArray(base.forwards.new) ||
72+
Array.isArray(base.forwards.old) ||
73+
Array.isArray(base.backwards.new) ||
74+
Array.isArray(base.backwards.old)) {
75+
throw new Error('extend() can only handle animation objects, not arrays');
76+
}
77+
return {
78+
forwards: {
79+
old: Object.fromEntries([
80+
...Object.entries(base.forwards.old).map(([key, value]) => [map[key], timeString(value)]),
81+
...Object.entries(extension?.forwards?.old ?? {}),
82+
]),
83+
new: Object.fromEntries([
84+
...Object.entries(base.forwards.new).map(([key, value]) => [map[key], timeString(value)]),
85+
...Object.entries(extension?.forwards?.new ?? {}),
86+
]),
87+
},
88+
backwards: {
89+
old: Object.fromEntries([
90+
...Object.entries(base.backwards.old).map(([key, value]) => [map[key], value]),
91+
...Object.entries(extension?.backwards?.old ?? {}),
92+
]),
93+
new: Object.fromEntries([
94+
...Object.entries(base.backwards.new).map(([key, value]) => [map[key], value]),
95+
...Object.entries(extension?.backwards?.new ?? {}),
96+
]),
97+
},
98+
};
99+
};
87100

88101
const framesMap = {};
89-
export function setKeyframes(name: string, css: string) {
90-
framesMap[name] = css;
91-
}
92-
export function getKeyframes(name: string) {
93-
return framesMap[name];
94-
}
102+
export const setKeyframes = (name: string, css: string) => framesMap[name] = css;
103+
export const getKeyframes = (name: string) => framesMap[name];
104+
95105
const stylesMap = {};
96-
export function setStyles(name: string, css: string) {
97-
stylesMap[name] = css;
98-
}
99-
export function getStyles(name: string) {
100-
return stylesMap[name];
101-
}
106+
export const setStyles = (name: string, css: string) => stylesMap[name] = css;
107+
export const getStyles = (name: string) => stylesMap[name];

animations/swing.ts

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
import './swing.css';
2-
import { NamedAnimationPairs, extend, maybeScopedStyleSheet, setKeyframes, setStyles } from './animation-style';
2+
import { type NamedAnimationPairs, extend, styleSheet, setKeyframes, setStyles } from './animation-style';
33
import type { TransitionAnimation, TransitionDirectionalAnimations } from 'astro';
44

55
type SwingKeyframeParameter = {
66
axis?: { x?: number; y?: number; z?: number; };
7-
angle?: { grow?: string; shrink?: string; };
8-
midOpacity?: number;
9-
toOpacity?: number;
7+
angle?: { leave?: string; enter?: string; };
8+
opacity?: { mid?: number; to?: number; };
109
};
1110

1211
type CustomSwingOptions = {
13-
keyframes?: SwingKeyframeParameter;
12+
keyframes?: SwingKeyframeParameter | string;
1413
base?: AnimationProperties;
15-
animations?: NamedAnimationPairs;
14+
extensions?: NamedAnimationPairs;
1615
};
1716

1817

1918
export const genKeyframes = (
20-
name: string,
19+
keyframeNamePrefix: string,
2120
x: number = 0,
2221
y: number = 0,
2322
z: number = 0,
24-
growAngle = "90deg",
25-
shrinkAngle = "-90deg",
23+
leaveAngle = "90deg",
24+
enterAngle = "-90deg",
2625
midOpacity: number = 1,
2726
toOpacity: number = 0
28-
) =>
29-
`
30-
@keyframes ${name}FwdSwingOut {
27+
) => setKeyframes(keyframeNamePrefix, `
28+
@keyframes ${keyframeNamePrefix}FwdSwingOut {
3129
from {
3230
transform: rotate3d(${x}, ${y}, ${z}, 0);
3331
opacity: 1;
@@ -36,13 +34,13 @@ export const genKeyframes = (
3634
opacity: ${midOpacity};
3735
}
3836
to {
39-
transform: rotate3d(${x}, ${y}, ${z}, ${growAngle});
37+
transform: rotate3d(${x}, ${y}, ${z}, ${leaveAngle});
4038
opacity: ${toOpacity};
4139
}
4240
}
43-
@keyframes ${name}FwdSwingIn {
41+
@keyframes ${keyframeNamePrefix}FwdSwingIn {
4442
from {
45-
transform: rotate3d(${x}, ${y}, ${z}, ${shrinkAngle});
43+
transform: rotate3d(${x}, ${y}, ${z}, ${enterAngle});
4644
opacity: ${toOpacity};
4745
}
4846
50% {
@@ -53,7 +51,7 @@ export const genKeyframes = (
5351
opacity: 1;
5452
}
5553
}
56-
@keyframes ${name}BwdSwingOut {
54+
@keyframes ${keyframeNamePrefix}BwdSwingOut {
5755
from {
5856
transform: rotate3d(${x}, ${y}, ${z}, 0);
5957
opacity: 1;
@@ -62,13 +60,13 @@ export const genKeyframes = (
6260
opacity: ${midOpacity};
6361
}
6462
to {
65-
transform: rotate3d(${x}, ${y}, ${z}, ${shrinkAngle});
63+
transform: rotate3d(${x}, ${y}, ${z}, ${enterAngle});
6664
opacity: ${toOpacity};
6765
}
6866
}
69-
@keyframes ${name}BwdSwingIn {
67+
@keyframes ${keyframeNamePrefix}BwdSwingIn {
7068
from {
71-
transform: rotate3d(${x}, ${y}, ${z}, ${growAngle});
69+
transform: rotate3d(${x}, ${y}, ${z}, ${leaveAngle});
7270
opacity: ${toOpacity};
7371
}
7472
50% {
@@ -78,12 +76,12 @@ export const genKeyframes = (
7876
transform: rotate3d(${x}, ${y}, ${z}, 0);
7977
opacity: 1;
8078
}
81-
}`;
79+
}`);
8280

8381
export type AnimationProperties = Omit<TransitionAnimation, "name">;
8482

8583
export const swing = (animation?: AnimationProperties) => namedSwing('', animation);
86-
export const namedSwing = (name: string, animation?: AnimationProperties) => {
84+
export const namedSwing = (keyframeNamePrefix: string, animation?: AnimationProperties) => {
8785
const common = {
8886
easing: 'ease-in-out',
8987
fillMode: 'both',
@@ -92,34 +90,38 @@ export const namedSwing = (name: string, animation?: AnimationProperties) => {
9290
};
9391

9492
const forwards = {
95-
old: { ...common, name: `${name}FwdSwingOut` },
96-
new: { delay: common.duration, ...common, name: `${name}FwdSwingIn` },
93+
old: { ...common, name: `${keyframeNamePrefix}FwdSwingOut` },
94+
new: { delay: common.duration, ...common, name: `${keyframeNamePrefix}FwdSwingIn` },
9795
};
9896

9997
const backwards = {
100-
old: { ...common, name: `${name}BwdSwingOut` },
101-
new: { delay: common.duration, ...common, name: `${name}BwdSwingIn` },
98+
old: { ...common, name: `${keyframeNamePrefix}BwdSwingOut` },
99+
new: { delay: common.duration, ...common, name: `${keyframeNamePrefix}BwdSwingIn` },
102100
};
103101
return { forwards, backwards } as TransitionDirectionalAnimations;
104102
};
105103

106104

107105
export const customSwing = (
108-
name: string,
106+
transitionName: string,
109107
options: CustomSwingOptions,
110108
scope?: string
111109
) => {
112-
const { keyframes, base, animations: extensions } = options;
113-
const animations = extend(namedSwing(name, base), extensions ?? {});
114-
const axis = keyframes?.axis ?? { y: 1 };
110+
const { keyframes, base, extensions } = options;
115111

116-
setKeyframes(
117-
name,
118-
genKeyframes(name, axis?.x, axis?.y, axis?.z, keyframes?.angle?.grow, keyframes?.angle?.shrink, keyframes?.midOpacity, keyframes?.toOpacity)
119-
);
112+
let keyframeNamePrefix: string;
120113

121-
let { scope: finalScope, styles } = maybeScopedStyleSheet(name, scope, animations);
122-
setStyles(name, styles);
114+
if (typeof keyframes === 'string') {
115+
keyframeNamePrefix = keyframes;
116+
} else {
117+
keyframeNamePrefix = transitionName;
118+
const axis = keyframes?.axis ?? { y: 1 };
119+
genKeyframes(keyframeNamePrefix, axis?.x, axis?.y, axis?.z, keyframes?.angle?.leave, keyframes?.angle?.enter, keyframes?.opacity?.mid, keyframes?.opacity?.to);
120+
}
121+
122+
const animations = extend(namedSwing(keyframeNamePrefix, base), extensions ?? {});
123+
const { scope: finalScope, styles } = styleSheet({ transitionName, scope, animations });
124+
setStyles(transitionName, styles);
123125
return finalScope;
124126
};
125127

0 commit comments

Comments
 (0)