import { Layout, Txt, makeScene2D } from "@motion-canvas/2d"; import { Direction, all, beginSlide, createRef, makeRef, slideTransition, waitFor, } from "@motion-canvas/core"; import { FunctionBox } from "../components/function_box"; import { theme } from "../theme"; import { PEOPLE, Person, PersonI } from "../components/person"; export const daysUntilNextDate = (date: Date): number => { const today = new Date(); const nextBirthday = new Date( today.getFullYear(), date.getMonth(), date.getDate(), ); if (today > nextBirthday) nextBirthday.setFullYear(today.getFullYear() + 1); const timeDiff = nextBirthday.getTime() - today.getTime(); return Math.ceil(timeDiff / (1000 * 3600 * 24)); }; export interface CardI { message: string; deliverInDays: number; } export const birthdayCardsFor = (people: PersonI[]): CardI[] => { const cards: CardI[] = []; for (const person of people) { const age = new Date().getFullYear() - person.birthday.getFullYear(); const ending = ({ 1: "st", 2: "nd", 3: "rd" } as Record)[ parseInt(Array.from(age.toString()).at(-1)) ] ?? "th"; const message = `Happy ${age}${ending} birthday, ${person.name}!\n` + "I can't believe it's already been a year!"; const deliverInDays = daysUntilNextDate(person.birthday); cards.push({ deliverInDays, message }); } return cards; }; const birthdayCardsSrc = `export interface CardI { message: string; deliverInDays: number; } const birthdayCardsFor = (people: PersonI[]): CardI[] => { const cards: CardI[] = []; for (const person of people) { const age = new Date().getFullYear() - person.birthday.getFullYear(); const ageEnding = ( { 1: "st", 2: "nd", 3: "rd" } as Record )[parseInt(Array.from(age.toString()).at(-1))] ?? "th"; const message = \`Happy \${age}\${ageEnding} birthday, \${person.name}!\\n\` + "I can't believe it's already been a year!"; const deliverInDays = daysUntilNextDate(person.birthday); cards.push({ deliverInDays, message }); } return cards; };`; export default makeScene2D(function* (view) { const layout = createRef(); const date = createRef(); const functionBox = createRef(); const people: Person[] = []; const peopleLayout: Layout[] = []; const peopleText: Txt[] = []; view.add( , ); yield* all(slideTransition(Direction.Left), functionBox().showCode(0.75)); yield* functionBox().reset(0.1); yield* beginSlide("Show code"); yield* functionBox().reset(0.1); yield* all( functionBox().hideCode(0.8), functionBox().setInputs( [ { val: PEOPLE, node: ( {PEOPLE.map((person) => ( ))} ), }, ], 0.8, ), ); yield* beginSlide("Show people"); yield* functionBox().propogateInput(0.6); yield* functionBox().propogateOutput(0.6); yield* beginSlide("Generate birthday cards"); yield* functionBox().opacity(0, 0.5); functionBox().remove(); view.add( {PEOPLE.map((person, i) => ( ))} , ); yield* layout().opacity(1, 0.5); const cards = birthdayCardsFor(PEOPLE); for (let i = 1; i <= 365 + 1; i++) { const day = new Date(Date.now() + i * 24 * 60 * 60 * 1000); yield* waitFor(0.02); yield date().text(day.toDateString()); const peoplesBirthday = cards .map((x, i) => [x, i] as [CardI, number]) .filter(([{ deliverInDays }]) => deliverInDays === i) .map(([_, i]) => i); yield* all( ...peoplesBirthday.map((p) => { const text = peopleText[p]; return all( text.text("🗓️🎂\n" + cards[p].message, 0), text.fontSize(20, 0.5), ); }), ); yield* all( ...peoplesBirthday.map((p) => { const layout = peopleLayout[p]; return layout.gap(0, 0.5); }), ); yield* all( ...peoplesBirthday.map((p) => { const person = people[p]; const text = peopleText[p]; return all( text.opacity(0, 0.5), text.fontSize(0, 0.5), person.emit("🥳", 0.8, false), ); }), ); } yield* beginSlide("See their reactions"); yield* all( date().fontSize(0, 0.5), date().opacity(0, 0.65), ...people.map((person) => person.opacity(0, 0.5)), ); });