hated-by-life-itself

This commit is contained in:
2023-03-23 17:20:35 +08:00
parent 47df8a11c2
commit 3cba73a339
15 changed files with 388 additions and 349 deletions

View File

@@ -1,15 +1,4 @@
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
@@ -23,7 +12,7 @@ jobs:
- run: echo $WORKFLOW_INPUT > input-props.json
env:
WORKFLOW_INPUT: ${{ toJson(github.event.inputs) }}
- run: npm run build -- --props="./input-props.json"
- run: npm run build"
- uses: actions/upload-artifact@v2
with:
name: out.mp4

View File

@@ -4,7 +4,7 @@
"description": "My Remotion video",
"scripts": {
"start": "remotion preview",
"build": "remotion render HelloWorld out/video.mp4",
"build": "remotion render MovingLyrics out/video.mp4",
"upgrade": "remotion upgrade",
"test": "eslint src --ext ts,tsx,js,jsx && tsc"
},

BIN
public/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
public/music.webm Normal file

Binary file not shown.

View File

@@ -1,65 +0,0 @@
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>
);
};

View File

@@ -1,54 +0,0 @@
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>
);
};

View File

@@ -1,35 +0,0 @@
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>
);
};

View File

@@ -1,71 +0,0 @@
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

@@ -1,26 +0,0 @@
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>
);
};

View File

@@ -1,58 +0,0 @@
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

@@ -1,6 +0,0 @@
// 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';

43
src/MovingLyrics.tsx Normal file
View File

@@ -0,0 +1,43 @@
import {staticFile, Audio, Img} from 'remotion';
import {AbsoluteFill, Sequence, useCurrentFrame} from 'remotion';
import VerticalText from './VerticalText';
import lyrics from './lyrics';
export const MovingLyrics: React.FC = () => {
const current = useCurrentFrame();
const beginMoveFrame = 2374;
const moveSpeed = 0.7;
const moveShift = (current - beginMoveFrame) * moveSpeed;
// A <AbsoluteFill> is just a absolutely positioned <div>!
return (
<AbsoluteFill>
<Sequence name="background">
<Img src={staticFile('bg.jpg')} />
</Sequence>
<Sequence>
{lyrics.map((lyric) => (
<VerticalText
text={lyric.content}
from={lyric.from}
to={lyric.to}
x={lyric.x}
y={
current < beginMoveFrame
? lyric.y
: lyric.y +
moveShift -
(lyric.from < beginMoveFrame
? 0
: (lyric.from - beginMoveFrame) * moveSpeed)
}
fontSize={lyric.fontSize}
disappear={lyric.disappear}
/>
))}
</Sequence>
<Audio src={staticFile('music.webm')} />
</AbsoluteFill>
);
};

View File

@@ -1,6 +1,5 @@
import {Composition} from 'remotion';
import {HelloWorld} from './HelloWorld';
import {Logo} from './HelloWorld/Logo';
import {MovingLyrics} from './MovingLyrics';
// Each <Composition> is an entry in the sidebar!
@@ -10,25 +9,10 @@ export const RemotionRoot: React.FC = () => {
<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}
id="MovingLyrics"
component={MovingLyrics}
durationInFrames={16408}
fps={59.94}
width={1920}
height={1080}
/>

64
src/VerticalText.tsx Normal file
View File

@@ -0,0 +1,64 @@
import React from 'react';
import {
AbsoluteFill,
random,
Sequence,
useCurrentFrame,
interpolate,
} from 'remotion';
type Props = {
text: string;
fontSize?: number;
from: number;
to: number;
x: number;
y: number;
disappear?: number;
};
const VerticalText: React.FC<Props> = ({
text,
fontSize = 39,
from,
to,
x,
y,
disappear,
}) => {
const current = useCurrentFrame();
const textArray = Array.from(text);
const currentEndIndex = ~~(
(textArray.length * (current - from)) /
(to - from)
);
const r = random(current - (current % 2));
const opacity = interpolate(current, [from, from + 8], [0, 1]);
return (
<AbsoluteFill>
<Sequence
name={textArray.slice(0, 6).join('')}
from={from}
durationInFrames={disappear ? disappear - from : 4096}
style={{
opacity,
top: `${1080 - y}px`,
left: x,
letterSpacing: '0.2em',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-end',
width: 'min-content',
fontSize: `${fontSize}px`,
writingMode: 'vertical-lr',
overflow: 'hidden',
}}
>
{textArray.slice(0, currentEndIndex).join('')}
{currentEndIndex < text.length && textArray[~~(r * textArray.length)]}
</Sequence>
</AbsoluteFill>
);
};
export default VerticalText;

274
src/lyrics.ts Normal file
View File

@@ -0,0 +1,274 @@
interface Lyric {
content: string;
x: number;
y: number;
from: number;
to: number;
fontSize?: number;
disappear?: number;
}
const lyrics: Lyric[] = [
{
content: '命に嫌われている。',
from: 649,
to: 650,
x: 1605,
y: 1011,
fontSize: 90,
},
{content: '「不要說想死這種話啊」', from: 1226, to: 1327, x: 1365, y: 1032},
{
content: '「不要放棄,好好活下去啊」',
from: 1370,
to: 1459,
x: 1265,
y: 805,
},
{
content: '說是那樣的歌曲才是正確的,實在有夠可笑',
from: 1496,
to: 1708,
x: 1060,
y: 921,
},
{content: '事實上自己死了也無所謂', from: 1758, to: 1888, x: 819, y: 998},
{content: '身邊的人死去卻會感到悲傷', from: 1920, to: 2030, x: 648, y: 626},
{content: '說著「因為我不喜歡那樣」', from: 2065, to: 2196, x: 364, y: 983},
{content: '被當作任性', from: 2220, to: 2295, x: 127, y: 419},
{content: '其他人只要活著就怎樣都好', from: 2375, to: 2487, x: 496, y: 697},
{content: '厭惡著誰也像是追隨流行般', from: 2523, x: 1160, y: 558, to: 2650},
{
content: '儘管如此仍說了「安穩地活著吧」',
from: 2661,
x: 947,
y: 825,
to: 2800,
},
{content: '還真是美妙的事啊', from: 2809, x: 814, y: 600, to: 2936},
{content: '在螢幕的另一頭有誰死了', from: 2951, x: 234, y: 617, to: 3078},
{content: '哀嘆著那樣的某人歌唱著', from: 3095, x: 364, y: 600, to: 3220},
{content: '而被那歌曲所感化的少年', from: 3247, x: 507, y: 598, to: 3368},
{content: '手持刀子狂奔不已', from: 3378, x: 663, y: 564, to: 3476},
{
content: '我們被生命厭惡著',
from: 3496,
x: 1391,
y: 942,
to: 3618,
fontSize: 90,
},
{content: '強加上了價值觀與自私的', from: 3654, x: 1655, y: 731, to: 3741},
{content: '那總是想去殺了誰的歌曲', from: 3775, x: 1032, y: 746, to: 3903},
{content: '輕易地透過無線電波流瀉而出', from: 3937, x: 853, y: 752, to: 4053},
{content: '我們被生命厭惡著', from: 4066, x: 138, y: 795, to: 4196},
{content: '輕率地就說出想死這種話', from: 4214, x: 283, y: 756, to: 4339},
{content: '輕率地看待生命的我們', from: 4369, x: 443, y: 778, to: 4520},
{content: '被生命厭惡著', from: 4525, x: 925, y: 677, to: 4620, fontSize: 80},
{
content: '謳唱著沒有錢所以今天也無所事事地睡過去',
from: 5248,
x: 1557,
y: 758,
to: 5477,
},
{
content: '找不出活著有什麼意義,自知一切都是徒勞但還是呼吸著',
from: 5539,
x: 1327,
y: 850,
to: 5805,
fontSize: 32,
},
{content: '「好寂寞」', from: 5829, x: 876, y: 867, to: 5845, fontSize: 150},
{content: '怎麼能說這種話', from: 5856, x: 738, y: 299, to: 5912},
{content: '把這個傷口顯露出來真的好嗎', from: 5952, x: 520, y: 882, to: 6058},
{
content: '懷著那樣的倔強今天也一個人孤獨入眠',
from: 6118,
x: 302,
y: 801,
to: 6333,
},
{
content: '曾是少年的我們',
from: 6405,
x: 1730,
y: 611,
to: 6507,
},
{content: '終有一日會轉為青年', from: 6526, x: 1489, y: 656, to: 6654},
{
content: '逐漸衰老終有一日會有如枯萎的葉',
from: 6695,
x: 1156,
y: 729,
to: 6811,
},
{content: '不為人所知地腐朽而去', from: 6826, x: 1032, y: 630, to: 6946},
{content: '有天得到不死的軀體', from: 6983, x: 661, y: 703, to: 7087},
{content: '一生都不會走向死亡地活著', from: 7132, x: 469, y: 630, to: 7240},
{content: '妄想著那般科幻的劇情', from: 7268, x: 200, y: 600, to: 7390},
{content: '自己就算死了也無所謂', from: 7560, x: 1666, y: 705, to: 7680},
{content: '卻希望身邊的人們能活下去', from: 7701, x: 1504, y: 782, to: 7823},
{
content: '懷抱著這樣矛盾的想法活下去',
from: 7850,
x: 1322,
y: 810,
to: 7966,
},
{content: '會被斥責的', from: 7993, x: 1207, y: 733, to: 8105},
{
content: '「正確的事物就讓它正確地存在」',
from: 8123,
x: 321,
y: 820,
to: 8248,
},
{content: '「如果不想死的話就活下去」', from: 8274, x: 543, y: 889, to: 8400},
{content: '若陷入悲傷也無所謂的話', from: 8415, x: 770, y: 865, to: 8526},
{content: '就永遠一個人笑著啊', from: 8564, x: 1011, y: 724, to: 8647},
{content: '我們被生命厭惡著', from: 8673, x: 1408, y: 782, to: 8789},
{content: '連幸福的意義也不明白', from: 8832, x: 1226, y: 778, to: 8931},
{content: '就只會憎恨生長的環境', from: 8960, x: 652, y: 692, to: 9070},
{content: '如此輕易地詛咒那些過去', from: 9118, x: 219, y: 823, to: 9222},
{content: '我們被生命厭惡著', from: 9227, x: 422, y: 756, to: 9352},
{content: '淨是把永別掛在嘴邊', from: 9408, x: 947, y: 818, to: 9510},
{
content: '連真正的生死離別都不懂的我們',
from: 9544,
x: 1662,
y: 835,
to: 9686,
},
{
content: '被生命厭惡著',
from: 9715,
x: 1324,
y: 842,
to: 9802,
fontSize: 80,
},
{content: '幸福', from: 10406, x: 1077, y: 594, to: 10450},
{content: '別離', from: 10488, x: 1254, y: 500, to: 10525},
{content: '愛情', from: 10576, x: 945, y: 485, to: 10603},
{content: '友情', from: 10627, x: 1169, y: 331, to: 10670},
{content: '全是滑稽美夢中的玩笑話', from: 10710, x: 755, y: 814, to: 10807},
{content: '全是金錢買得到的東西', from: 10842, x: 652, y: 808, to: 10953},
{
content: '明天',
from: 11001,
x: 1687,
y: 765,
to: 11023,
fontSize: 139,
},
{
content: '也許就會迎來死亡',
from: 11034,
x: 1538,
y: 970,
to: 11125,
fontSize: 100,
},
{
content: '一切',
from: 11153,
x: 121,
y: 686,
to: 11175,
fontSize: 139,
},
{
content: '也許都是白費力氣',
from: 11183,
x: 353,
y: 899,
to: 11268,
fontSize: 100,
},
{content: '早晨', from: 11291, x: 1019, y: 746, to: 11309},
{content: '夜晚', from: 11317, x: 1019, y: 611, to: 11333},
{content: '春天', from: 11351, x: 851, y: 637, to: 11374},
{content: '秋天', from: 11397, x: 851, y: 511, to: 11415},
{
content: '都一如既往地會有著誰在哪兒死去',
from: 11433,
x: 936,
y: 639,
to: 11546,
},
{
content: '夢想也好明天也罷我什麼都不需要',
from: 11560,
x: 1399,
y: 647,
to: 11672,
},
{content: '只要你還活著就好', from: 11696, x: 1297, y: 609, to: 11811},
{content: '啊是這樣啊', from: 11849, x: 1184, y: 476, to: 11873},
{content: '我真正', from: 11908, x: 1079, y: 427, to: 11952},
{content: '想唱的是這樣的歌啊', from: 12017, x: 659, y: 701, to: 12107},
{content: '被生命厭惡著', from: 12137, x: 1677, y: 489, to: 12240},
{content: '最後總有一天會死亡', from: 12275, x: 1566, y: 560, to: 12378},
{content: '不論是你', from: 12427, x: 1448, y: 425, to: 12459},
{content: '不論是我', from: 12488, x: 1339, y: 385, to: 12519},
{
content: '終有一日都會有如枯萎的葉腐朽而去',
from: 12545,
x: 509,
y: 808,
to: 12675,
},
{
content: '儘管如此我們還是奮力地活著',
from: 12700,
x: 1120,
y: 831,
to: 12817,
fontSize: 55,
},
{
content: '奮力地擁抱著生命活下去',
from: 12835,
x: 767,
y: 887,
to: 12962,
fontSize: 55,
},
{content: '扼殺著', from: 12981, x: 1685, y: 575, to: 13011, fontSize: 139},
{content: '掙扎著', from: 13024, x: 1410, y: 570, to: 13054, fontSize: 139},
{content: '歡笑著', from: 13063, x: 208, y: 677, to: 13088, fontSize: 139},
{content: '背負著', from: 13094, x: 584, y: 564, to: 13122, fontSize: 139},
{content: '活下去', from: 13139, x: 910, y: 1038, to: 13155, fontSize: 139},
{
content: '活下去',
from: 13182,
x: 910,
y: 519,
to: 13196,
fontSize: 139,
disappear: 13280,
},
{content: '活下去', from: 13216, x: 385, y: 513, to: 13234, fontSize: 139},
{content: '活下去', from: 13242, x: 1213, y: 637, to: 13257, fontSize: 139},
{
content: '活下去啊',
from: 13287,
x: 910,
y: 609,
to: 13296,
fontSize: 139,
},
{content: '命に嫌われている', from: 14462, x: 1288, y: 701, to: 14559},
{content: 'カンザキイオリ', from: 14608, x: 641, y: 624, to: 14704},
];
export default lyrics;