From 7b6d5f1666e428c37c936bd6b01323c3a6399ac6 Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Tue, 14 Nov 2023 19:08:14 -0800 Subject: Initial commit - barebone lrc player --- src/app/App.tsx | 91 ++++++++++++++++++++++++++++++++ src/app/components/Control.tsx | 42 +++++++++++++++ src/app/components/Video.tsx | 0 src/app/data.ts | 66 +++++++++++++++++++++++ src/app/globals.css | 23 -------- src/app/layout.tsx | 14 ++--- src/app/page.tsx | 116 ++--------------------------------------- src/app/use_timer.ts | 33 ++++++++++++ 8 files changed, 240 insertions(+), 145 deletions(-) create mode 100644 src/app/App.tsx create mode 100644 src/app/components/Control.tsx create mode 100644 src/app/components/Video.tsx create mode 100644 src/app/data.ts create mode 100644 src/app/use_timer.ts (limited to 'src') diff --git a/src/app/App.tsx b/src/app/App.tsx new file mode 100644 index 0000000..ebe2d33 --- /dev/null +++ b/src/app/App.tsx @@ -0,0 +1,91 @@ +import { CSSProperties, useCallback, useRef, useEffect, useState } from "react"; +import styled, { css } from "styled-components"; +import { Lrc, LrcLine } from "react-lrc"; +import { LRC } from "./data"; + +const Root = styled.div` + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + + display: flex; + flex-direction: column; +`; +const lrcStyle: CSSProperties = { + flex: 1, + minHeight: 0, + overflow: 'hidden !important' +}; +const Line = styled.div<{ $active: boolean; $next: boolean }>` + min-height: 10px; + padding: 14px 30px; + + font-size: 40px; + font-family : "Roboto", sans-serif; + font-weight: 500; + text-align: center; + color: rgb(72,72,72); + + background: linear-gradient(to right, rgba(0,0,0,0) 50%, rgb(200, 190, 190) 50%); + background-size: 200% 100%; + background-position: right bottom; + + ${({ $active }) => $active && css` + color: black; + font-weight: 700; + background-position: left bottom; + color: rgb(50, 50, 50); + `} +`; + +function App() { + const [currentMillisecond, setCurrentMillisecond] = useState(0); + + const videoRef = useRef(null); + + useEffect(() => { + const video = videoRef.current; + if (!video) return; + + const syncLrcWithVideo = () => { + const offset = 400; + setCurrentMillisecond((video.currentTime * 1000)+offset); + }; + + video.addEventListener('timeupdate', syncLrcWithVideo); + + return () => { + video.removeEventListener('timeupdate', syncLrcWithVideo); + }; + }, [setCurrentMillisecond]); + + const lineRenderer = useCallback( + ({ active, line: { content } }: { active: boolean; line: LrcLine }) => { + const next = active && content === ''; + return {content} + }, + [] + ); + + return ( + +
+ +
+
+
+
+ ); +} + +export default App; diff --git a/src/app/components/Control.tsx b/src/app/components/Control.tsx new file mode 100644 index 0000000..2ad2339 --- /dev/null +++ b/src/app/components/Control.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +function Control({ + onPlay, + onPause, + onReset, + current, + setCurrent, + recoverAutoScrollImmediately, +}: { + onPlay: () => void; + onPause: () => void; + onReset: () => void; + current: number; + setCurrent: (c: number) => void; + recoverAutoScrollImmediately: () => void; +}) { + return ( +
+ + + + setCurrent(Number(event.target.value))} + className="input" + /> + +
+ ); +} + +export default Control; \ No newline at end of file diff --git a/src/app/components/Video.tsx b/src/app/components/Video.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/app/data.ts b/src/app/data.ts new file mode 100644 index 0000000..eff48cf --- /dev/null +++ b/src/app/data.ts @@ -0,0 +1,66 @@ +export const LRC = `[00:11.53]Another day, I wander +[00:13.85]Without escape, I ponder +[00:15.98]A million questions +[00:17.25]I don't need to find an answer for +[00:20.59]Like, is it worth the hassle? +[00:22.78]Or, is it worth the pain? +[00:24.23]Look inside the mirror and say to myself +[00:28.17]Am I enough? +[00:30.81]Oh, am I worth it? +[00:35.570] +[00:37.670]I see the way you notice +[00:39.980]All of my fragile moments +[00:42.210]A part of me is still uncertain I should let you in +[00:46.670]But as it flows and passes +[00:48.800]The time will just confirm +[00:50.150]That when you're here with me +[00:51.940]I can just let go +[00:54.560]Now suddenly, the clouds clear out +[00:59.070]All my worries disappear +[01:01.070]And all the stars, they feel so near +[01:03.340]I could almost reach out right now +[01:07.580]Because of you, feel myself again +[01:11.980]You helped me realize what was here to do +[01:16.480]Because of you, I'm feeling real again +[01:20.840]Now I know where I should go +[01:22.940]You got me walking back to hope +[01:25.090]One step at a time +[01:34.560]I can't explain the feeling +[01:36.630]A sort of, kind of healing +[01:38.980]A different wave of love that travels +[01:41.530]Through your precious words +[01:43.370]At times, I circle back, but +[01:45.600]I guess I just forget +[01:46.980]That when I'm down and low +[01:48.940]I could count on you +[01:51.460]Now suddenly, the clouds clear out +[01:55.760]All my worries disappear +[01:57.810]And all the stars, they feel so near +[02:00.300]I could almost reach out right now +[02:05.530]Because of you, I feel myself again +[02:09.930]You helped me realize what I was here to do +[02:14.370]Because of you, I'm feeling real again +[02:18.520]Now I know where I should go +[02:20.640]You got me walking back to hope +[02:22.250]A step at a time +[02:25.940]♪ +[02:41.030]What can I say but thank you +[02:43.400]Surrounded by some angels +[02:45.190]Honestly, it's hard for me to live without +[02:49.800]Tomorrow's bringing something new +[02:53.160]I know for certain, it's worth it +[02:56.560]All thanks to you +[03:08.840]Because of you, I feel myself again +[03:13.240]You helped me realize what I was here to do +[03:17.450]Because of you, I'm feeling real again +[03:21.930]Now I know where I should go +[03:23.980]You got me walking back to hope +[03:26.390]Because of you, I feel myself again +[03:30.700]You helped me realize what I was here to do +[03:34.940]Because of you, I'm feeling real again +[03:39.310]Now I know where I should go +[03:41.570]You got me walking back to hope +[03:43.860]One step at a time +[03:51.980]A little closer +[03:55.580]Every step gets closer +[03:58.330]`; diff --git a/src/app/globals.css b/src/app/globals.css index fd81e88..a90f074 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,26 +2,3 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 40e027f..a14e64f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,12 +1,6 @@ -import type { Metadata } from 'next' -import { Inter } from 'next/font/google' -import './globals.css' - -const inter = Inter({ subsets: ['latin'] }) - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', } export default function RootLayout({ @@ -16,7 +10,7 @@ export default function RootLayout({ }) { return ( - {children} + {children} ) } diff --git a/src/app/page.tsx b/src/app/page.tsx index b973266..e4a8169 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,113 +1,5 @@ -import Image from 'next/image' +"use client" +import React from 'react'; +import App from './App'; -export default function Home() { - return ( -
-
-

- Get started by editing  - src/app/page.tsx -

-
- - By{' '} - Vercel Logo - -
-
- -
- Next.js Logo -
- -
- -

- Docs{' '} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{' '} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
- - -

- Templates{' '} - - -> - -

-

- Explore starter templates for Next.js. -

-
- - -

- Deploy{' '} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
-
- ) -} +export default App; \ No newline at end of file diff --git a/src/app/use_timer.ts b/src/app/use_timer.ts new file mode 100644 index 0000000..487591d --- /dev/null +++ b/src/app/use_timer.ts @@ -0,0 +1,33 @@ +import { useEffect, useState, useCallback } from "react"; + +function useTimer(speed = 1) { + const [paused, setPaused] = useState(true); + const play = useCallback(() => setPaused(false), []); + const pause = useCallback(() => setPaused(true), []); + + const [currentMillisecond, setCurrentMillisecond] = useState(0); + const reset = useCallback(() => setCurrentMillisecond(0), []); + + useEffect(() => { + if (!paused) { + let last = Date.now(); + const timer = window.setInterval(() => { + const now = Date.now(); + setCurrentMillisecond((cm) => cm + (now - last) * speed); + last = now; + }, 97); + return () => window.clearInterval(timer); + } + }, [paused, speed]); + + return { + currentMillisecond, + setCurrentMillisecond, + reset, + paused, + play, + pause + }; +} + +export default useTimer; \ No newline at end of file -- cgit v1.2.3