Create new Remotion video

This commit is contained in:
2023-03-23 12:56:34 +08:00
commit 47df8a11c2
19 changed files with 3164 additions and 0 deletions

3
.eslintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "@remotion"
}

30
.github/workflows/render-video.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Render video
on:
workflow_dispatch:
inputs:
titleText:
description: "Which text should it say?"
required: true
default: "Welcome to Remotion"
titleColor:
description: "Which color should it be in?"
required: true
default: "black"
jobs:
render:
name: Render video
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- uses: actions/setup-node@main
- run: sudo apt update
- run: sudo apt install ffmpeg
- run: npm i
- run: echo $WORKFLOW_INPUT > input-props.json
env:
WORKFLOW_INPUT: ${{ toJson(github.event.inputs) }}
- run: npm run build -- --props="./input-props.json"
- uses: actions/upload-artifact@v2
with:
name: out.mp4
path: out/video.mp4

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
node_modules
dist
.DS_Store
.env
# Ignore the output video from Git but not videos you import into src/.
out

14
.prettierrc Normal file
View File

@@ -0,0 +1,14 @@
{
"singleQuote": true,
"bracketSpacing": false,
"jsxBracketSameLine": false,
"useTabs": true,
"overrides": [
{
"files": ["*.yml"],
"options": {
"singleQuote": false
}
}
]
}

9
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"editor.tabSize": 2,
"typescript.tsdk": "node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.organizeImports": false,
"source.fixAll": true
},
"typescript.enablePromptUseWorkspaceTsdk": true
}

54
README.md Normal file
View File

@@ -0,0 +1,54 @@
# Remotion video
<p align="center">
<a href="https://github.com/remotion-dev/logo">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/remotion-dev/logo/raw/main/animated-logo-banner-dark.gif">
<img alt="Animated Remotion Logo" src="https://github.com/remotion-dev/logo/raw/main/animated-logo-banner-light.gif">
</picture>
</a>
</p>
Welcome to your Remotion project!
## Commands
**Install Dependencies**
```console
yarn
```
**Start Preview**
```console
yarn start
```
**Render video**
```console
yarn build
```
**Upgrade Remotion**
```console
yarn run upgrade
```
## Docs
Get started with Remotion by reading the [fundamentals page](https://www.remotion.dev/docs/the-fundamentals).
## Help
We provide help [on our Discord server](https://discord.gg/6VzzNDwUwV).
## Issues
Found an issue with Remotion? [File an issue here](https://github.com/remotion-dev/remotion/issues/new).
## License
Notice that for some entities a company license is needed. Read [the terms here](https://github.com/remotion-dev/remotion/blob/main/LICENSE.md).

28
package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "hated-by-life-itself",
"version": "1.0.0",
"description": "My Remotion video",
"scripts": {
"start": "remotion preview",
"build": "remotion render HelloWorld out/video.mp4",
"upgrade": "remotion upgrade",
"test": "eslint src --ext ts,tsx,js,jsx && tsc"
},
"repository": {},
"license": "UNLICENSED",
"dependencies": {
"@remotion/cli": "3.3.78",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"remotion": "3.3.78"
},
"devDependencies": {
"@remotion/eslint-config": "3.3.78",
"@types/react": "^18.0.26",
"@types/web": "^0.0.86",
"eslint": "^8.30.0",
"prettier": "^2.8.1",
"typescript": "^4.9.4"
},
"packageManager": "yarn@1.22.19"
}

9
remotion.config.ts Normal file
View File

@@ -0,0 +1,9 @@
// See all configuration options: https://remotion.dev/docs/config
// Each option also is available as a CLI flag: https://remotion.dev/docs/cli
// Note: The configuration file does only apply if you render via the CLI !
import {Config} from 'remotion';
Config.setImageFormat('jpeg');
Config.setOverwriteOutput(true);

65
src/HelloWorld.tsx Normal file
View File

@@ -0,0 +1,65 @@
import {spring} from 'remotion';
import {
AbsoluteFill,
interpolate,
Sequence,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
import {Logo} from './HelloWorld/Logo';
import {Subtitle} from './HelloWorld/Subtitle';
import {Title} from './HelloWorld/Title';
export const HelloWorld: React.FC<{
titleText: string;
titleColor: string;
}> = ({titleText, titleColor}) => {
const frame = useCurrentFrame();
const {durationInFrames, fps} = useVideoConfig();
// Animate from 0 to 1 after 25 frames
const logoTranslationProgress = spring({
frame: frame - 25,
fps,
config: {
damping: 100,
},
});
// Move the logo up by 150 pixels once the transition starts
const logoTranslation = interpolate(
logoTranslationProgress,
[0, 1],
[0, -150]
);
// Fade out the animation at the end
const opacity = interpolate(
frame,
[durationInFrames - 25, durationInFrames - 15],
[1, 0],
{
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
}
);
// A <AbsoluteFill> is just a absolutely positioned <div>!
return (
<AbsoluteFill style={{backgroundColor: 'white'}}>
<AbsoluteFill style={{opacity}}>
<AbsoluteFill style={{transform: `translateY(${logoTranslation}px)`}}>
<Logo />
</AbsoluteFill>
{/* Sequences can shift the time for its children! */}
<Sequence from={35}>
<Title titleText={titleText} titleColor={titleColor} />
</Sequence>
{/* The subtitle will only enter on the 75th frame. */}
<Sequence from={75}>
<Subtitle />
</Sequence>
</AbsoluteFill>
</AbsoluteFill>
);
};

54
src/HelloWorld/Arc.tsx Normal file
View File

@@ -0,0 +1,54 @@
import {useState} from 'react';
import {random, useVideoConfig} from 'remotion';
import {COLOR_1, COLOR_2} from './constants';
const getCircumferenceOfArc = (rx: number, ry: number) => {
return Math.PI * 2 * Math.sqrt((rx * rx + ry * ry) / 2);
};
const rx = 135;
const ry = 300;
const cx = 960;
const cy = 540;
const arcLength = getCircumferenceOfArc(rx, ry);
const strokeWidth = 30;
export const Arc: React.FC<{
progress: number;
rotation: number;
rotateProgress: number;
}> = ({progress, rotation, rotateProgress}) => {
const {width, height} = useVideoConfig();
// Each svg Id must be unique to not conflict with each other
const [gradientId] = useState(() => String(random(null)));
return (
<svg
viewBox={`0 0 ${width} ${height}`}
style={{
position: 'absolute',
transform: `rotate(${rotation * rotateProgress}deg)`,
}}
>
<defs>
<linearGradient id={gradientId} x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stopColor={COLOR_1} />
<stop offset="100%" stopColor={COLOR_2} />
</linearGradient>
</defs>
<ellipse
cx={cx}
cy={cy}
rx={rx}
ry={ry}
fill="none"
stroke={`url(#${gradientId})`}
strokeDasharray={arcLength}
strokeDashoffset={arcLength - arcLength * progress}
strokeLinecap="round"
strokeWidth={strokeWidth}
/>
</svg>
);
};

35
src/HelloWorld/Atom.tsx Normal file
View File

@@ -0,0 +1,35 @@
import {useState} from 'react';
import {random, useVideoConfig} from 'remotion';
import {COLOR_1, COLOR_2} from './constants';
export const Atom: React.FC<{
scale: number;
}> = ({scale}) => {
const config = useVideoConfig();
// Each SVG ID must be unique to not conflict with each other
const [gradientId] = useState(() => String(random(null)));
return (
<svg
viewBox={`0 0 ${config.width} ${config.height}`}
style={{
position: 'absolute',
transform: `scale(${scale})`,
}}
>
<defs>
<linearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor={COLOR_1} />
<stop offset="100%" stopColor={COLOR_2} />
</linearGradient>
</defs>
<circle
r={70}
cx={config.width / 2}
cy={config.height / 2}
fill={`url(#${gradientId})`}
/>
</svg>
);
};

71
src/HelloWorld/Logo.tsx Normal file
View File

@@ -0,0 +1,71 @@
import {
AbsoluteFill,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
import {Arc} from './Arc';
import {Atom} from './Atom';
export const Logo: React.FC = () => {
const videoConfig = useVideoConfig();
const frame = useCurrentFrame();
const development = spring({
config: {
damping: 100,
mass: 0.5,
},
fps: videoConfig.fps,
frame,
});
const rotationDevelopment = spring({
config: {
damping: 100,
mass: 0.5,
},
fps: videoConfig.fps,
frame,
});
const scale = spring({
frame,
config: {
mass: 0.5,
},
fps: videoConfig.fps,
});
const logoRotation = interpolate(
frame,
[0, videoConfig.durationInFrames],
[0, 360]
);
return (
<AbsoluteFill
style={{
transform: `scale(${scale}) rotate(${logoRotation}deg)`,
}}
>
<Arc
rotateProgress={rotationDevelopment}
progress={development}
rotation={30}
/>
<Arc
rotateProgress={rotationDevelopment}
rotation={90}
progress={development}
/>
<Arc
rotateProgress={rotationDevelopment}
rotation={-30}
progress={development}
/>
<Atom scale={rotationDevelopment} />
</AbsoluteFill>
);
};

View File

@@ -0,0 +1,26 @@
import React from 'react';
import {interpolate, useCurrentFrame} from 'remotion';
import {COLOR_1, FONT_FAMILY} from './constants';
const subtitle: React.CSSProperties = {
fontFamily: FONT_FAMILY,
fontSize: 40,
textAlign: 'center',
position: 'absolute',
bottom: 140,
width: '100%',
};
const codeStyle: React.CSSProperties = {
color: COLOR_1,
};
export const Subtitle: React.FC = () => {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 30], [0, 1]);
return (
<div style={{...subtitle, opacity}}>
Edit <code style={codeStyle}>src/Root.tsx</code> and save to reload.
</div>
);
};

58
src/HelloWorld/Title.tsx Normal file
View File

@@ -0,0 +1,58 @@
import React from 'react';
import {spring, useCurrentFrame, useVideoConfig} from 'remotion';
import {FONT_FAMILY} from './constants';
const title: React.CSSProperties = {
fontFamily: FONT_FAMILY,
fontWeight: 'bold',
fontSize: 100,
textAlign: 'center',
position: 'absolute',
bottom: 160,
width: '100%',
};
const word: React.CSSProperties = {
marginLeft: 10,
marginRight: 10,
display: 'inline-block',
};
export const Title: React.FC<{
titleText: string;
titleColor: string;
}> = ({titleText, titleColor}) => {
const videoConfig = useVideoConfig();
const frame = useCurrentFrame();
const words = titleText.split(' ');
return (
<h1 style={title}>
{words.map((t, i) => {
const delay = i * 5;
const scale = spring({
fps: videoConfig.fps,
frame: frame - delay,
config: {
damping: 200,
},
});
return (
<span
key={t}
style={{
...word,
color: titleColor,
transform: `scale(${scale})`,
}}
>
{t}
</span>
);
})}
</h1>
);
};

View File

@@ -0,0 +1,6 @@
// Change any of these to update your video live.
export const COLOR_1 = '#86A8E7';
export const COLOR_2 = '#91EAE4';
export const FONT_FAMILY = 'SF Pro Text, Helvetica, Arial, sans-serif';

37
src/Root.tsx Normal file
View File

@@ -0,0 +1,37 @@
import {Composition} from 'remotion';
import {HelloWorld} from './HelloWorld';
import {Logo} from './HelloWorld/Logo';
// Each <Composition> is an entry in the sidebar!
export const RemotionRoot: React.FC = () => {
return (
<>
<Composition
// You can take the "id" to render a video:
// npx remotion render src/index.ts <id> out/video.mp4
id="HelloWorld"
component={HelloWorld}
durationInFrames={150}
fps={30}
width={1920}
height={1080}
// You can override these props for each render:
// https://www.remotion.dev/docs/parametrized-rendering
defaultProps={{
titleText: 'Welcome to Remotion',
titleColor: 'black',
}}
/>
{/* Mount any React component to make it show up in the sidebar and work on it individually! */}
<Composition
id="OnlyLogo"
component={Logo}
durationInFrames={150}
fps={30}
width={1920}
height={1080}
/>
</>
);
};

7
src/index.ts Normal file
View File

@@ -0,0 +1,7 @@
// This is your entry file! Refer to it when you render:
// npx remotion render <entry-file> HelloWorld out/video.mp4
import {registerRoot} from 'remotion';
import {RemotionRoot} from './Root';
registerRoot(RemotionRoot);

14
tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"jsx": "react-jsx",
"outDir": "./dist",
"strict": true,
"noEmit": true,
"lib": ["es2015"],
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

2637
yarn.lock Normal file

File diff suppressed because it is too large Load Diff