From 7c2f10b513ef7ded8415cf83c73dbeccb8c94186 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 5 Jul 2023 12:55:26 -0400 Subject: [PATCH] Implement JS animation API --- packages/astro/src/@types/astro.ts | 4 +- .../astro/src/runtime/server/transition.ts | 133 ++++++++++++++---- packages/astro/src/transitions/index.ts | 2 +- 3 files changed, 111 insertions(+), 28 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 79e73d470abab..49185cdbfd8cb 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -74,6 +74,8 @@ export interface TransitionDirectionalAnimations { backwards: TransitionAnimationPair; } +export type TransitionAnimationValue = 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations; + // Allow users to extend this for astro-jsx.d.ts // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface AstroClientDirectives {} @@ -88,7 +90,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; } diff --git a/packages/astro/src/runtime/server/transition.ts b/packages/astro/src/runtime/server/transition.ts index 4cbc1985f5f08..1c679527b2c39 100644 --- a/packages/astro/src/runtime/server/transition.ts +++ b/packages/astro/src/runtime/server/transition.ts @@ -1,7 +1,13 @@ -import type { SSRResult } from '../../@types/astro'; +import type { + SSRResult, + TransitionAnimation, + TransitionDirectionalAnimations, + TransitionAnimationValue, +} from '../../@types/astro'; import { markHTMLString } from './escape.js'; +import { slide, fade } from '../../transitions/index.js'; -const animations = { +const animationsOld = { 'slide': { old: '--astro-animate-old-slideout', new: '--astro-animate-new-slidein', @@ -28,12 +34,23 @@ function createTransitionScope(result: SSRResult, hash: string) { const num = incrementTransitionNumber(result); return `astro-${hash}-${num}`; } -export function renderTransition(result: SSRResult, hash: string, animationName: string | undefined, transitionName: string) { - // Default animation is morph - if(!animationName) { - animationName = "morph"; +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; + } + } } - const animation = animations[animationName as keyof typeof animations]; const scope = createTransitionScope(result, hash); @@ -43,25 +60,22 @@ export function renderTransition(result: SSRResult, hash: string, animationName: } const styles = markHTMLString(``) @@ -69,3 +83,70 @@ export function renderTransition(result: SSRResult, hash: string, animationName: return scope; } + +type AnimationBuilder = { + toString(): string; + [key: string]: string[] | ((k: string) => string); +} + +function addAnimationProperty(builder: AnimationBuilder, prop: string, value: string | number) { + let arr = builder[prop]; + if(Array.isArray(arr)) { + arr.push(value.toString()); + } else { + builder[prop] = [value.toString()]; + } +} + +function animationBuilder(): AnimationBuilder { + return { + toString() { + let out = ''; + for(let k in this) { + let value = this[k]; + if(Array.isArray(value)) { + out += `\n\t${k}: ${value.join(', ')};` + } + } + return out; + } + }; +} + +function stringifyAnimation(anim: TransitionAnimation | TransitionAnimation[]): string { + if(Array.isArray(anim)) { + return stringifyAnimations(anim); + } else { + return stringifyAnimations([anim]); + } +} + +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)); + } + if(anim.easing) { + addAnimationProperty(builder, 'animation-timing-function', anim.easing); + } + if(anim.direction) { + addAnimationProperty(builder, 'animation-direction', anim.direction); + } + if(anim.delay) { + addAnimationProperty(builder, 'animation-delay', anim.delay); + } + if(anim.fillMode) { + addAnimationProperty(builder, 'animation-fill-mode', anim.fillMode); + } + addAnimationProperty(builder, 'animation-name', anim.name); + } + + return builder.toString(); +} + +function toTimeValue(num: number | string) { + return typeof num === 'number' ? num + 'ms' : num; +} diff --git a/packages/astro/src/transitions/index.ts b/packages/astro/src/transitions/index.ts index a8d4d5aac860d..e8ebb54023e04 100644 --- a/packages/astro/src/transitions/index.ts +++ b/packages/astro/src/transitions/index.ts @@ -42,7 +42,7 @@ export function fade({ duration }: { duration?: string | number; -}): TransitionDirectionalAnimations { +} = {}): TransitionDirectionalAnimations { const anim = { old: { name: 'astroFadeInOut',