working animations
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useGSAP } from "@gsap/react";
|
import { useGSAP } from "@gsap/react";
|
||||||
import { useLayoutEffect,
|
import { useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
useRef, type ReactNode } from "react";
|
useRef, type ReactNode } from "react";
|
||||||
import { useGsapContext } from "~/app/_providers/GsapProvicer";
|
import { useGsapContext } from "~/app/_providers/GsapProvicer";
|
||||||
import { SplitText } from "gsap/SplitText";
|
import { SplitText } from "gsap/SplitText";
|
||||||
@@ -7,7 +8,7 @@ import gsap from 'gsap'
|
|||||||
const AnimateTextIn = ({children,animation="type",position}:{children:ReactNode,animation?:"type"|"slide",position:gsap.Position}) => {
|
const AnimateTextIn = ({children,animation="type",position}:{children:ReactNode,animation?:"type"|"slide",position:gsap.Position}) => {
|
||||||
const el = useRef<HTMLDivElement>(null)
|
const el = useRef<HTMLDivElement>(null)
|
||||||
const gsapContext = useGsapContext();
|
const gsapContext = useGsapContext();
|
||||||
useGSAP(() => {
|
useEffect(() => {
|
||||||
console.log("aniamte text with:",position)
|
console.log("aniamte text with:",position)
|
||||||
const tl = gsap.timeline();
|
const tl = gsap.timeline();
|
||||||
const chars = new SplitText(el.current,{type:'chars'})
|
const chars = new SplitText(el.current,{type:'chars'})
|
||||||
|
|||||||
@@ -102,7 +102,10 @@ export default function AnimatedBackgroundContainer({
|
|||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
const isDark = resolvedTheme === "dark";
|
let isDark = resolvedTheme === "dark";
|
||||||
|
if (resolvedTheme == undefined) {
|
||||||
|
isDark = true;
|
||||||
|
}
|
||||||
const palette = isDark ? PALETTES.dark : PALETTES.light;
|
const palette = isDark ? PALETTES.dark : PALETTES.light;
|
||||||
|
|
||||||
/* Spawn particles */
|
/* Spawn particles */
|
||||||
@@ -264,7 +267,6 @@ export default function AnimatedBackgroundContainer({
|
|||||||
minHeight: "100vh",
|
minHeight: "100vh",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
backgroundColor: palette.base,
|
|
||||||
transition: "background-color 0.6s ease",
|
transition: "background-color 0.6s ease",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,19 +7,16 @@ const AnimatedPageTitle = (
|
|||||||
) => {
|
) => {
|
||||||
const el = useRef<HTMLHeadingElement>(null)
|
const el = useRef<HTMLHeadingElement>(null)
|
||||||
const gsapContext = useGsapContext();
|
const gsapContext = useGsapContext();
|
||||||
useGSAP(() => {
|
useEffect(() => {
|
||||||
console.log("add animated title with:",position)
|
console.log("add animated title with:",position)
|
||||||
const tl = gsap.timeline();
|
|
||||||
tl.addLabel("title")
|
|
||||||
const split = new SplitText(el.current, { type: "chars" })
|
const split = new SplitText(el.current, { type: "chars" })
|
||||||
tl.from(el.current, { opacity: 0 })
|
gsapContext?.addAnimation(gsap.to(el.current, { opacity: 100 }),position)
|
||||||
tl.from(split.chars, {
|
gsapContext?.addAnimation(gsap.from(split.chars, {
|
||||||
stagger: 0.05, rotate: -90, opacity: 0, x: -10
|
stagger: 0.05, rotate: -90, opacity: 0, x: -10
|
||||||
}, '>')
|
}),'>')
|
||||||
gsapContext?.addAnimation(tl,position)
|
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<h1 className="text-4xl opacity-100 font-bold text-balance w-full" ref={el}> {text} </h1>
|
<h1 className="text-4xl opacity-0 font-bold text-balance w-full" ref={el}> {text} </h1>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,18 @@ import { Moon, Sun } from "lucide-react"
|
|||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
type Props = {activeTheme:string|undefined}
|
type Props = {activeTheme:string|undefined}
|
||||||
const ThemeIcon = (props:Props) => {
|
const ThemeIcon = (props:Props) => {
|
||||||
if (props.activeTheme == "dark") {
|
return (
|
||||||
return (<Sun/>)
|
<>
|
||||||
} else {
|
{props.activeTheme && props.activeTheme == 'dark' &&
|
||||||
return (<Moon/>)
|
<Sun/>
|
||||||
}
|
}
|
||||||
|
{props.activeTheme && props.activeTheme == 'light' &&
|
||||||
|
<Moon/>
|
||||||
|
}
|
||||||
|
{!props.activeTheme &&
|
||||||
|
<Sun/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
export default ThemeIcon;
|
export default ThemeIcon;
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import ThemeIcon from "./ThemeIcon"
|
|||||||
|
|
||||||
export function ThemeSwitch() {
|
export function ThemeSwitch() {
|
||||||
const { setTheme, theme } = useTheme()
|
const { setTheme, theme } = useTheme()
|
||||||
|
if (!theme) {
|
||||||
|
setTheme('dark')
|
||||||
|
}
|
||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
setTheme(theme == "dark" ? "light" : "dark")
|
setTheme(theme == "dark" ? "light" : "dark")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,11 @@ export default function GsapProvider({ children }: { children: ReactNode }) {
|
|||||||
if (!tl.current) {
|
if (!tl.current) {
|
||||||
tl.current = gsap.timeline({ paused: true })
|
tl.current = gsap.timeline({ paused: true })
|
||||||
}
|
}
|
||||||
console.log("resuming timeline:",tl.current)
|
|
||||||
return () => { console.log("gsap cleanup") }
|
return () => { console.log("gsap cleanup") }
|
||||||
})
|
})
|
||||||
|
|
||||||
const addAnimation = useCallback((animation: gsap.core.TimelineChild, position: gsap.Position) => {
|
const addAnimation = useCallback((animation: gsap.core.TimelineChild, position: gsap.Position) => {
|
||||||
console.log("add animation to:", position)
|
console.log("add animation to:", position, tl.current !== undefined)
|
||||||
tl.current?.add(animation, position);
|
tl.current?.add(animation, position);
|
||||||
},[])
|
},[])
|
||||||
const resetTimeline = useCallback(() => {
|
const resetTimeline = useCallback(() => {
|
||||||
@@ -41,6 +40,7 @@ export default function GsapProvider({ children }: { children: ReactNode }) {
|
|||||||
tl.current = gsap.timeline({paused:true})
|
tl.current = gsap.timeline({paused:true})
|
||||||
},[])
|
},[])
|
||||||
const resumeTimeline = useCallback(() => {
|
const resumeTimeline = useCallback(() => {
|
||||||
|
console.log("resuming timeline:",tl.current)
|
||||||
tl.current?.resume()
|
tl.current?.resume()
|
||||||
},[])
|
},[])
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import * as React from "react"
|
|
||||||
import { ThemeProvider as NextThemesProvider } from "next-themes"
|
import { ThemeProvider as NextThemesProvider } from "next-themes"
|
||||||
|
|
||||||
export default function ThemeProvider({children}:{children: React.ReactNode}) {
|
export default function ThemeProvider({children}:{children: React.ReactNode}) {
|
||||||
const [mounted,setMounted] = React.useState(false)
|
|
||||||
React.useEffect(() => {
|
|
||||||
setMounted(true)
|
|
||||||
})
|
|
||||||
if (mounted) {
|
|
||||||
return (
|
return (
|
||||||
<NextThemesProvider disableTransitionOnChange nonce="test" attribute="class" defaultTheme="dark">
|
<NextThemesProvider disableTransitionOnChange attribute="class" defaultTheme="dark">
|
||||||
{children}
|
{children}
|
||||||
</NextThemesProvider>
|
</NextThemesProvider>
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useGSAP } from "@gsap/react";
|
import { useGSAP } from "@gsap/react";
|
||||||
import { useLayoutEffect,
|
import { useEffect,
|
||||||
|
useEffectEvent,
|
||||||
|
useLayoutEffect,
|
||||||
useRef } from "react";
|
useRef } from "react";
|
||||||
import { trpc } from "~/app/_trpc/Client";
|
import { trpc } from "~/app/_trpc/Client";
|
||||||
import * as Card from "~/components/ui/card";
|
import * as Card from "~/components/ui/card";
|
||||||
@@ -8,21 +10,20 @@ import { useGsapContext } from "../_providers/GsapProvicer";
|
|||||||
import AnimatedPageTitle from "../_components/Animated/AnimatedPageTitle";
|
import AnimatedPageTitle from "../_components/Animated/AnimatedPageTitle";
|
||||||
import { Spinner } from "~/components/ui/spinner";
|
import { Spinner } from "~/components/ui/spinner";
|
||||||
import AnimateTextIn from "../_components/Animated/AnimateIn";
|
import AnimateTextIn from "../_components/Animated/AnimateIn";
|
||||||
import gsap from 'gsap'
|
|
||||||
export default function MusicPage() {
|
export default function MusicPage() {
|
||||||
const { data: tracks, isLoading } = trpc.music.list.useQuery();
|
const { data: tracks, isLoading } = trpc.music.list.useQuery();
|
||||||
const container = useRef<HTMLDivElement>(null)
|
|
||||||
const gsapContext = useGsapContext();
|
const gsapContext = useGsapContext();
|
||||||
useGSAP(() => {
|
useEffect(() => {
|
||||||
|
if (tracks) {
|
||||||
gsapContext?.resumeTimeline()
|
gsapContext?.resumeTimeline()
|
||||||
|
}
|
||||||
return () => {
|
return () => {
|
||||||
console.log("page cleanup")
|
console.log("page cleanup")
|
||||||
gsapContext?.resetTimeline()
|
gsapContext?.resetTimeline()
|
||||||
}
|
}
|
||||||
});
|
},[tracks]);
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<div ref={container} className="w-full h-full max-w-4xl mx-auto px-4 py-8 flex flex-col gap-4">
|
<div className="w-full h-full max-w-4xl mx-auto px-4 py-8 flex flex-col gap-4">
|
||||||
<AnimatedPageTitle position={0} text="Just Some Music I Made"/>
|
<AnimatedPageTitle position={0} text="Just Some Music I Made"/>
|
||||||
<AnimateTextIn position={0.5}>
|
<AnimateTextIn position={0.5}>
|
||||||
<div className="flex flex-row h-8 content-center items-center">
|
<div className="flex flex-row h-8 content-center items-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user