Remotion: Make a video of the original Kanto pokemon *from code* Part 2
So now, let's hop into Composition.tsx
.
Composition.tsx
First, let's make a div
and display hello in it to test if it is working!
export const MyComposition = () => {
return <div>hello</div>;
};
Now, let's make the background black and make the width of the div
100%
export const MyComposition = () => {
return <div style={{background: 'black', width: '100%'}}>hello</div>;
};
If you remember, we changed the color and font for the h1
last time. Let's put the hello in h1
.
Data fetching
Docs: remotion.dev/docs/data-fetching
Here's an example from the docs:
import { useEffect, useCallback, useState } from "react";
import { continueRender, delayRender } from "remotion";
export const MyVideo = () => {
const [data, setData] = useState(null);
const [handle] = useState(() => delayRender());
const fetchData = useCallback(async () => {
const response = await fetch("http://example.com/api");
const json = await response.json();
setData(json);
continueRender(handle);
}, [handle]);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<div>
{data ? (
<div>This video has data from an API! {JSON.stringify(data)}</div>
) : null}
</div>
);
};
Now what should we do? Let's create a function, and get the data of the first 151 Pokemon from PokeApi.
Let's import:
import {useEffect, useCallback, useState} from 'react';
import {continueRender, delayRender} from 'remotion';
const [data, setData] = useState < any[] > ([]);
const [handle] = useState(() => delayRender());
const fetchData = useCallback(
async (num: number) => {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${num}`);
const json = await response.json();
let arr = data;
arr.push({
json: json,
num: num
});
setData(arr);
continueRender(handle);
},
[handle]
);
useEffect(() => {
for (let index = 0; index < 151; index++) {
fetchData(index + 1);
}
}, [fetchData]);
So here, we are getting the data, but also giving it a num
property, so that it stays in the same order.
Now, let's show data
in the code.
<h1>{JSON.stringify(data)}</h1>
Images
Now, let's display images. For testing, I'll show an image for every Pokemon first.
{
data
.sort((a, b) => (a.num > b.num ? 1 : b.num > a.num ? -1 : 0))
.map((e) => {
return <img src = {
e.json.sprites.front_default
}
/>;
})
}
Now how to show a pokemon for 1 second 🤔.
Showing Pokemons once in a time
First, we need the current frame. For that, we have useCurrentFrame()
in Remotion.
const frame = useCurrentFrame();
Also, let's import the Img
tag so that it updates on its own.
So the current Pokemon data should be data.sort((a, b) => (a.num > b.num ? 1 : b.num > a.num ? -1 : 0))[Math.floor(frame / 20) ]
right? Because our video is in 20fps. So the image should be:
<Img
src = {
data.sort((a, b) =>
a.num > b.num ? 1 : b.num > a.num ? -1 : 0
)[Math.floor(frame / 20)]?.json?.sprites?.front_default
}
/>
Use optional chaining so that it doesn't give an error in the starting when the data hasn't loaded
But instead, let's use an SVG. You can find it like this: ?.json?.sprites?.other.dream_world?.front_default
Now you'll see something like this:
We're getting something cool now! Now let's also make an h1
and change its text to the Pokemon name.
<h1>
{
data.sort((a, b) => (a.num > b.num ? 1 : b.num > a.num ? -1 : 0))[
Math.floor(frame / 20)
]?.json?.name
}
</h1>
Hope you like it so far! See you in Part 3, the last part!
Code till now
Video.tsx
import {Composition} from 'remotion';
import {MyComposition} from './Composition';
export const RemotionVideo: React.FC = () => {
return (
<>
<Composition
id="Empty"
component={MyComposition}
durationInFrames={3020}
fps={20}
width={1920}
height={1080}
/>
</>
);
};
Composition.tsx
import './font.css';
import {useEffect, useCallback, useState} from 'react';
import {continueRender, delayRender, Img, useCurrentFrame} from 'remotion';
export const MyComposition = () => {
const frame = useCurrentFrame();
const [data, setData] = useState<any[]>([]);
const [handle] = useState(() => delayRender());
const fetchData = useCallback(
async (num: number) => {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${num}`);
const json = await response.json();
let arr = data;
arr.push({
json: json,
num: num,
});
setData(arr);
continueRender(handle);
},
[handle]
);
useEffect(() => {
for (let index = 0; index < 151; index++) {
fetchData(index + 1);
}
}, [fetchData]);
return (
<div style={{background: 'black', width: '100%'}}>
<Img
src={
data.sort((a, b) => (a.num > b.num ? 1 : b.num > a.num ? -1 : 0))[
Math.floor(frame / 20)
]?.json?.sprites?.other?.dream_world?.front_default
}
/>
<h1>
{
data.sort((a, b) => (a.num > b.num ? 1 : b.num > a.num ? -1 : 0))[
Math.floor(frame / 20)
]?.json?.name
}
</h1>
</div>
);
}
font.css
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
h1 {
font-family: 'Press Start 2P';
color: white;
}