birthdays
This commit is contained in:
parent
9db4283a20
commit
c55d9d2832
15
public/img/profile.svg
Normal file
15
public/img/profile.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 72 72" id="emoji" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="color">
|
||||||
|
<path fill="#D0CFCE" stroke="none" d="M58,61c0,0,0-3-1-7c-1.2109-4.8457-4-8-10-8c-5,0-15,0-22,0c-6,0-8.7891,3.1543-10,8c-1,4-1,7-1,7H58z"/>
|
||||||
|
<path fill="#D0CFCE" stroke="none" d="M26,26c0,3.7246,0.5391,7.8086,2,10c1.8613,2.793,5.0176,4,8,4c3.0957,0,6.1367-1.207,8-4 c1.46-2.1914,2-6.2754,2-10c0-2.7935-1-12-10-12S26,21.3442,26,26z"/>
|
||||||
|
</g>
|
||||||
|
<g id="hair"/>
|
||||||
|
<g id="skin"/>
|
||||||
|
<g id="skin-shadow"/>
|
||||||
|
<g id="line">
|
||||||
|
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M58,60c0,0,0-2-1-6 c-1.2109-4.8457-4-8-10-8c-5,0-15,0-22,0c-6,0-8.7891,3.1543-10,8c-1,4-1,6-1,6"/>
|
||||||
|
<path fill="none" stroke="#000000" stroke-linejoin="round" stroke-width="2" d="M26,26c0,3.7246,0.5391,7.8086,2,10 c1.8613,2.793,5.0176,4,8,4c3.0957,0,6.1367-1.207,8-4c1.46-2.1914,2-6.2754,2-10c0-2.7935-1-12-10-12S26,21.3442,26,26z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
16
script.md
16
script.md
@ -84,7 +84,7 @@ def make_valentines_letters(people):
|
|||||||
|
|
||||||
Good! Now they will all share our love.
|
Good! Now they will all share our love.
|
||||||
|
|
||||||
But a few months goes by, and soon all their birthdays are coming up! So we make another black box to generate a letter, and in how many days we should give it to them:
|
[SPACE] But a few months goes by, and all our friends' birthdays are soon coming up (why are so many cs people born in June)! We don't want to make them sad, as we see here, so we make another black box to generate a letter, and in how many days we should give it to them:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def make_birthday_cards(people):
|
def make_birthday_cards(people):
|
||||||
@ -101,9 +101,11 @@ def make_birthday_cards(people):
|
|||||||
return cards
|
return cards
|
||||||
```
|
```
|
||||||
|
|
||||||
But this is getting annoying; what about Christmas, Thanksgiving, Easter, or another life event such as a wedding? Making this function to make a list of new cards, going through each person every single time is getting really tedious.
|
There, now, we can make sure our friends are happy on their birthdays!
|
||||||
|
|
||||||
To help, we create a bunch of black boxes that take a person and create them a card, specifically. Then we create a black box that takes a list of people and another black box and creates a new card for each person?
|
But, this is getting annoying; what about Christmas, Thanksgiving, or Easter? Making a new black box to make a list of new cards, going through each person, every single time to construct a list of cards, is getting really tedious.
|
||||||
|
|
||||||
|
What if we generalized this? We create a bunch of black boxes that take a person, and generate them a card, specifically; like a template for a card you could print off and fill in manually.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def valentine_letter(person):
|
def valentine_letter(person):
|
||||||
@ -116,7 +118,11 @@ def birthday_card(person):
|
|||||||
|
|
||||||
card = f"Happy Birthday {name}\nI can't believe you're already {newAge} years old!"
|
card = f"Happy Birthday {name}\nI can't believe you're already {newAge} years old!"
|
||||||
cards.append({ "message": card, "deliverInDays": daysUntilBirthday })
|
cards.append({ "message": card, "deliverInDays": daysUntilBirthday })
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, we can use a black box that takes a list of people, and applies this template to each person.
|
||||||
|
|
||||||
|
```
|
||||||
def buildCards(people, cardMaker):
|
def buildCards(people, cardMaker):
|
||||||
cards = []
|
cards = []
|
||||||
for person in people:
|
for person in people:
|
||||||
@ -128,7 +134,7 @@ people = [{"name": "Joseph", birthday: new Date()}, {"name": "DeeDee", birthday:
|
|||||||
buildCards(people, birthdayCard)
|
buildCards(people, birthdayCard)
|
||||||
```
|
```
|
||||||
|
|
||||||
The ability to pass a function around like this - like a variable - is what makes functions "first class". And the `buildCards` function takes a function as input, making it a "higher order function". (TODO: slides)
|
The ability in a language to pass a function around like this - like a variable - is what makes functions "first class". And the `buildCards` function takes a function as input, making it a "higher order function". (TODO: slides)
|
||||||
|
|
||||||
Functional Reproduction
|
Functional Reproduction
|
||||||
===
|
===
|
||||||
@ -250,7 +256,7 @@ But there are also downsides:
|
|||||||
+ Keeping immutability requires more computation (data structures)
|
+ Keeping immutability requires more computation (data structures)
|
||||||
+ More difficult to write (huge selling point for Object Oriented Programming; encapsulation)
|
+ More difficult to write (huge selling point for Object Oriented Programming; encapsulation)
|
||||||
|
|
||||||
But like all relationships, we need to compromise with our black boxes. Side effects are _unavoidable_ as programmers; when people press the button, they don't want the computation to just exist out there in the computer void. No! They want to see the pop up box, and the value updated on their account. We need to be able to mutate state where the tradeoff of extra compute time is unavoidable like in I/O or a relatively large database.
|
But like all relationships, we need to compromise with our black boxes. Side effects are effectively _unavoidable_ as programmers; when people press the button, they don't want the computation to just exist out there in the computer void. No! They want to see the pop up box, and the value updated on their account. We need to be able to mutate state where the tradeoff of extra compute time is unavoidable like in I/O or a relatively large database.
|
||||||
|
|
||||||
Writing Code Functionally
|
Writing Code Functionally
|
||||||
===
|
===
|
||||||
|
@ -20,7 +20,7 @@ export interface FunctionBoxProps extends NodeProps {
|
|||||||
|
|
||||||
arity?: number;
|
arity?: number;
|
||||||
|
|
||||||
isChild?: boolean;
|
isChild?: boolean; // todo: attach DNA
|
||||||
}
|
}
|
||||||
|
|
||||||
type FunctionArgs = { node?: Node; val: any }[];
|
type FunctionArgs = { node?: Node; val: any }[];
|
||||||
@ -59,7 +59,7 @@ export class FunctionBox extends Node {
|
|||||||
|
|
||||||
this.arity = props?.arity ?? 1;
|
this.arity = props?.arity ?? 1;
|
||||||
if (props.fn) {
|
if (props.fn) {
|
||||||
this.source = props.fn.toString();
|
this.source = props.source ?? props.fn.toString();
|
||||||
this.function = props.fn;
|
this.function = props.fn;
|
||||||
} else {
|
} else {
|
||||||
this.source = props?.source ?? `(x: number): number => x + 2`;
|
this.source = props?.source ?? `(x: number): number => x + 2`;
|
||||||
@ -273,7 +273,7 @@ export class FunctionBox extends Node {
|
|||||||
<CodeBlock
|
<CodeBlock
|
||||||
fontFamily={theme.font}
|
fontFamily={theme.font}
|
||||||
language="typescript"
|
language="typescript"
|
||||||
fontSize={15}
|
fontSize={this.outputFontSize}
|
||||||
code={JSON.stringify(output, null, 2)}
|
code={JSON.stringify(output, null, 2)}
|
||||||
></CodeBlock>,
|
></CodeBlock>,
|
||||||
);
|
);
|
||||||
|
105
src/components/person.tsx
Normal file
105
src/components/person.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { Layout, Node, NodeProps, SVG, Txt } from "@motion-canvas/2d";
|
||||||
|
import { theme } from "../theme";
|
||||||
|
import { all, createRef, waitFor } from "@motion-canvas/core";
|
||||||
|
|
||||||
|
export const PEOPLE: PersonI[] = [
|
||||||
|
{
|
||||||
|
name: "Alan Turing",
|
||||||
|
birthday: new Date("06/23/1912"),
|
||||||
|
color: theme.green.hex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Grace Hopper",
|
||||||
|
birthday: new Date("12/09/1906"),
|
||||||
|
color: theme.flamingo.hex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Edsger Dijkstra",
|
||||||
|
birthday: new Date("07/11/1930"),
|
||||||
|
color: theme.red.hex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Alonzo Church",
|
||||||
|
birthday: new Date("06/14/1912"),
|
||||||
|
color: theme.sapphire.hex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Margaret Hamilton",
|
||||||
|
birthday: new Date("12/09/1902"),
|
||||||
|
color: theme.yellow.hex,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const profileSrc = (color: string) => `
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 72 72" id="emoji" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="color">
|
||||||
|
<path fill="${color}" stroke="none" d="M58,61c0,0,0-3-1-7c-1.2109-4.8457-4-8-10-8c-5,0-15,0-22,0c-6,0-8.7891,3.1543-10,8c-1,4-1,7-1,7H58z"/>
|
||||||
|
<path fill="${color}" stroke="none" d="M26,26c0,3.7246,0.5391,7.8086,2,10c1.8613,2.793,5.0176,4,8,4c3.0957,0,6.1367-1.207,8-4 c1.46-2.1914,2-6.2754,2-10c0-2.7935-1-12-10-12S26,21.3442,26,26z"/>
|
||||||
|
</g>
|
||||||
|
<g id="line">
|
||||||
|
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M58,60c0,0,0-2-1-6 c-1.2109-4.8457-4-8-10-8c-5,0-15,0-22,0c-6,0-8.7891,3.1543-10,8c-1,4-1,6-1,6"/>
|
||||||
|
<path fill="none" stroke="#000000" stroke-linejoin="round" stroke-width="2" d="M26,26c0,3.7246,0.5391,7.8086,2,10 c1.8613,2.793,5.0176,4,8,4c3.0957,0,6.1367-1.207,8-4c1.46-2.1914,2-6.2754,2-10c0-2.7935-1-12-10-12S26,21.3442,26,26z"/>
|
||||||
|
</g>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
export interface PersonI {
|
||||||
|
name: string;
|
||||||
|
birthday: Date;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonProps extends NodeProps {
|
||||||
|
person: PersonI;
|
||||||
|
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Person extends Node {
|
||||||
|
private readonly svg = createRef<SVG>();
|
||||||
|
|
||||||
|
public constructor(props?: PersonProps) {
|
||||||
|
super({ ...props });
|
||||||
|
|
||||||
|
this.add(
|
||||||
|
<Layout direction="column" alignItems="center" layout>
|
||||||
|
<SVG
|
||||||
|
ref={this.svg}
|
||||||
|
svg={profileSrc(props.person.color)}
|
||||||
|
fill="green"
|
||||||
|
width={props.width ?? 150}
|
||||||
|
height={props.height ?? 150}
|
||||||
|
></SVG>
|
||||||
|
<Txt fontSize={20} fontFamily={theme.font} fill={theme.text.hex}>
|
||||||
|
{props.person.name}
|
||||||
|
</Txt>
|
||||||
|
</Layout>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public *emit(text: string, duration: number, cleanUp = true) {
|
||||||
|
const ref = createRef<Txt>();
|
||||||
|
|
||||||
|
this.insert(
|
||||||
|
<Txt
|
||||||
|
position={this.svg().bottomRight()}
|
||||||
|
ref={ref}
|
||||||
|
fontSize={0}
|
||||||
|
opacity={0}
|
||||||
|
fontFamily={theme.font}
|
||||||
|
fill={theme.text.hex}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Txt>,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
yield* all(ref().fontSize(40, duration), ref().opacity(1, duration));
|
||||||
|
if (cleanUp) {
|
||||||
|
yield* waitFor(duration);
|
||||||
|
yield* all(ref().fontSize(0, duration), ref().opacity(0, duration));
|
||||||
|
yield ref().remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/scenes/birthday_letters.meta
Normal file
5
src/scenes/birthday_letters.meta
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"timeEvents": [],
|
||||||
|
"seed": 1594477027
|
||||||
|
}
|
213
src/scenes/birthday_letters.tsx
Normal file
213
src/scenes/birthday_letters.tsx
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
const daysUntilNextBirthday = (birthDate: Date): number => {
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
|
const nextBirthday = new Date(
|
||||||
|
today.getFullYear(),
|
||||||
|
birthDate.getMonth(),
|
||||||
|
birthDate.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<number, string>)[
|
||||||
|
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 = daysUntilNextBirthday(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<number, string>
|
||||||
|
)[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 = daysUntilNextBirthday(person.birthday);
|
||||||
|
cards.push({ deliverInDays, message });
|
||||||
|
}
|
||||||
|
|
||||||
|
return cards;
|
||||||
|
};`;
|
||||||
|
|
||||||
|
export default makeScene2D(function* (view) {
|
||||||
|
const layout = createRef<Layout>();
|
||||||
|
const date = createRef<Txt>();
|
||||||
|
const functionBox = createRef<FunctionBox>();
|
||||||
|
const people: Person[] = [];
|
||||||
|
const peopleLayout: Layout[] = [];
|
||||||
|
const peopleText: Txt[] = [];
|
||||||
|
|
||||||
|
view.add(
|
||||||
|
<FunctionBox
|
||||||
|
ref={functionBox}
|
||||||
|
source={birthdayCardsSrc}
|
||||||
|
fn={birthdayCardsFor}
|
||||||
|
workingText="📝⚙"
|
||||||
|
outputFontSize={25}
|
||||||
|
></FunctionBox>,
|
||||||
|
);
|
||||||
|
|
||||||
|
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: (
|
||||||
|
<Layout direction="column" gap={5} layout>
|
||||||
|
{PEOPLE.map((person) => (
|
||||||
|
<Person person={person} />
|
||||||
|
))}
|
||||||
|
</Layout>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
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(
|
||||||
|
<Layout opacity={0} ref={layout} direction="column" gap={15} layout>
|
||||||
|
<Txt
|
||||||
|
fontSize={30}
|
||||||
|
fontFamily={theme.font}
|
||||||
|
fill={theme.text.hex}
|
||||||
|
ref={date}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Layout direction="row" gap={15}>
|
||||||
|
{PEOPLE.map((person, i) => (
|
||||||
|
<Layout
|
||||||
|
ref={makeRef(peopleLayout, i)}
|
||||||
|
alignItems="center"
|
||||||
|
direction="column"
|
||||||
|
gap={100}
|
||||||
|
>
|
||||||
|
<Person ref={makeRef(people, i)} person={person} />
|
||||||
|
<Txt
|
||||||
|
textAlign="center"
|
||||||
|
ref={makeRef(peopleText, i)}
|
||||||
|
fontSize={0}
|
||||||
|
fontFamily={theme.font}
|
||||||
|
fill={theme.text.hex}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
))}
|
||||||
|
</Layout>
|
||||||
|
</Layout>,
|
||||||
|
);
|
||||||
|
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)),
|
||||||
|
);
|
||||||
|
});
|
@ -6,6 +6,9 @@ import doctor from "./doctor?scene";
|
|||||||
import first_box from "./first_box?scene";
|
import first_box from "./first_box?scene";
|
||||||
import hungry_partner from "./hungry_partner?scene";
|
import hungry_partner from "./hungry_partner?scene";
|
||||||
import pure_functions from "./pure_functions?scene";
|
import pure_functions from "./pure_functions?scene";
|
||||||
|
import valentines_letters from "./valentines_letters?scene";
|
||||||
|
import birthday_letters from "./birthday_letters?scene";
|
||||||
|
import sad_people from "./sad_people?scene";
|
||||||
|
|
||||||
export const scenes = [
|
export const scenes = [
|
||||||
//title,
|
//title,
|
||||||
@ -14,6 +17,9 @@ export const scenes = [
|
|||||||
//flirtingwithfunctions,
|
//flirtingwithfunctions,
|
||||||
//doctor,
|
//doctor,
|
||||||
//first_box,
|
//first_box,
|
||||||
hungry_partner,
|
//hungry_partner,
|
||||||
pure_functions,
|
//pure_functions,
|
||||||
|
//valentines_letters,
|
||||||
|
sad_people,
|
||||||
|
birthday_letters,
|
||||||
];
|
];
|
||||||
|
5
src/scenes/sad_people.meta
Normal file
5
src/scenes/sad_people.meta
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"timeEvents": [],
|
||||||
|
"seed": 867976792
|
||||||
|
}
|
99
src/scenes/sad_people.tsx
Normal file
99
src/scenes/sad_people.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
|
||||||
|
import {
|
||||||
|
Direction,
|
||||||
|
all,
|
||||||
|
beginSlide,
|
||||||
|
createRef,
|
||||||
|
makeRef,
|
||||||
|
slideTransition,
|
||||||
|
waitFor,
|
||||||
|
} from "@motion-canvas/core";
|
||||||
|
import { theme } from "../theme";
|
||||||
|
import { PEOPLE, Person } from "../components/person";
|
||||||
|
import { CardI, birthdayCardsFor } from "./birthday_letters";
|
||||||
|
|
||||||
|
export default makeScene2D(function* (view) {
|
||||||
|
const date = createRef<Txt>();
|
||||||
|
const people: Person[] = [];
|
||||||
|
const peopleLayout: Layout[] = [];
|
||||||
|
const peopleText: Txt[] = [];
|
||||||
|
|
||||||
|
view.add(
|
||||||
|
<Layout direction="column" gap={15} layout>
|
||||||
|
<Txt
|
||||||
|
fontSize={30}
|
||||||
|
fontFamily={theme.font}
|
||||||
|
fill={theme.text.hex}
|
||||||
|
ref={date}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Layout direction="row" gap={15}>
|
||||||
|
{PEOPLE.map((person, i) => (
|
||||||
|
<Layout
|
||||||
|
ref={makeRef(peopleLayout, i)}
|
||||||
|
alignItems="center"
|
||||||
|
direction="column"
|
||||||
|
gap={100}
|
||||||
|
>
|
||||||
|
<Person ref={makeRef(people, i)} person={person} />
|
||||||
|
<Txt
|
||||||
|
textAlign="center"
|
||||||
|
ref={makeRef(peopleText, i)}
|
||||||
|
fontSize={0}
|
||||||
|
fontFamily={theme.font}
|
||||||
|
fill={theme.text.hex}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
))}
|
||||||
|
</Layout>
|
||||||
|
</Layout>,
|
||||||
|
);
|
||||||
|
yield* all(slideTransition(Direction.Right));
|
||||||
|
|
||||||
|
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.01);
|
||||||
|
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("🗓️🎂", 0), text.fontSize(50, 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)),
|
||||||
|
);
|
||||||
|
});
|
5
src/scenes/valentines_letters.meta
Normal file
5
src/scenes/valentines_letters.meta
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"timeEvents": [],
|
||||||
|
"seed": 3850509512
|
||||||
|
}
|
122
src/scenes/valentines_letters.tsx
Normal file
122
src/scenes/valentines_letters.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
|
||||||
|
import {
|
||||||
|
Direction,
|
||||||
|
all,
|
||||||
|
beginSlide,
|
||||||
|
createRef,
|
||||||
|
makeRef,
|
||||||
|
slideTransition,
|
||||||
|
} from "@motion-canvas/core";
|
||||||
|
|
||||||
|
import { FunctionBox } from "../components/function_box";
|
||||||
|
import { theme } from "../theme";
|
||||||
|
import { PEOPLE, Person } from "../components/person";
|
||||||
|
|
||||||
|
const valentineLetterGenerator = `
|
||||||
|
interface PersonI {
|
||||||
|
name: string;
|
||||||
|
birthday: Date;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
(function valentinesLettersFor(people: PersonI[]) {
|
||||||
|
const letters: string[] = [];
|
||||||
|
|
||||||
|
for (const person of people) {
|
||||||
|
const letter = "Dear, " + person.name + "\\n"
|
||||||
|
+ "Your smile lights up my world.\\n"
|
||||||
|
+ "Happy Valentine's Day!";
|
||||||
|
letters.push(letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return letters;
|
||||||
|
})`;
|
||||||
|
|
||||||
|
export default makeScene2D(function* (view) {
|
||||||
|
const layout = createRef<Layout>();
|
||||||
|
const functionBox = createRef<FunctionBox>();
|
||||||
|
const people: Person[] = [];
|
||||||
|
const peopleLayout: Layout[] = [];
|
||||||
|
const peopleText: Txt[] = [];
|
||||||
|
|
||||||
|
view.add(
|
||||||
|
<FunctionBox
|
||||||
|
ref={functionBox}
|
||||||
|
source={valentineLetterGenerator}
|
||||||
|
workingText="📝⚙"
|
||||||
|
outputFontSize={25}
|
||||||
|
></FunctionBox>,
|
||||||
|
);
|
||||||
|
|
||||||
|
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: (
|
||||||
|
<Layout direction="column" gap={5} layout>
|
||||||
|
{PEOPLE.map((person) => (
|
||||||
|
<Person person={person} />
|
||||||
|
))}
|
||||||
|
</Layout>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
0.8,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
yield* beginSlide("Show people");
|
||||||
|
|
||||||
|
yield* functionBox().propogateInput(0.6);
|
||||||
|
yield* functionBox().propogateOutput(0.6);
|
||||||
|
yield* beginSlide("Generate valentines letters");
|
||||||
|
|
||||||
|
yield* functionBox().opacity(0, 0.5);
|
||||||
|
functionBox().remove();
|
||||||
|
|
||||||
|
view.add(
|
||||||
|
<Layout opacity={0} ref={layout} direction="row" gap={15} layout>
|
||||||
|
{PEOPLE.map((person, i) => (
|
||||||
|
<Layout
|
||||||
|
ref={makeRef(peopleLayout, i)}
|
||||||
|
alignItems="center"
|
||||||
|
direction="column"
|
||||||
|
gap={100}
|
||||||
|
>
|
||||||
|
<Person ref={makeRef(people, i)} person={person} />
|
||||||
|
<Txt
|
||||||
|
ref={makeRef(peopleText, i)}
|
||||||
|
fontSize={0}
|
||||||
|
fontFamily={theme.font}
|
||||||
|
fill={theme.text.hex}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
))}
|
||||||
|
</Layout>,
|
||||||
|
);
|
||||||
|
yield* layout().opacity(1, 0.5);
|
||||||
|
|
||||||
|
yield* all(...peopleText.map((text) => text.text("💌", 0)));
|
||||||
|
yield* all(...peopleText.map((text) => text.fontSize(50, 0.5)));
|
||||||
|
yield* all(...peopleLayout.map((layout) => layout.gap(0, 0.5)));
|
||||||
|
yield* all(
|
||||||
|
...peopleText.map((text) =>
|
||||||
|
all(text.fontSize(0, 0.5), text.opacity(0, 0.5)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
yield* beginSlide("Give people valentines letters");
|
||||||
|
|
||||||
|
yield* all(...people.map((person) => person.emit("😊", 0.5)));
|
||||||
|
|
||||||
|
yield* beginSlide("See their reactions");
|
||||||
|
yield* all(...people.map((person) => person.opacity(0, 0.5)));
|
||||||
|
});
|
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "@motion-canvas/2d/tsconfig.project.json",
|
"include": ["src"],
|
||||||
"include": ["src"]
|
"compilerOptions": {
|
||||||
|
"target": "es2022"
|
||||||
|
},
|
||||||
|
"extends": "@motion-canvas/2d/tsconfig.project.json"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user