mirror of
https://github.com/ClipFusion-org/clipfusion.git
synced 2025-08-03 16:55:08 +00:00
Project Library
title now fades when scrolling up
This commit is contained in:
parent
acd0762f82
commit
35f4084833
@ -32,6 +32,7 @@ import { Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTi
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
|
||||
import SidebarTriggerAdjustable from "@/components/sidebar-trigger-adjustable";
|
||||
import ScrollFadingTitle from "@/components/scroll-fading-title";
|
||||
|
||||
type SortingType = "byCreationDate"
|
||||
| "byEditDate"
|
||||
@ -491,8 +492,10 @@ export default function Home(): ReactNode {
|
||||
<div className="p-5">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<StaticSidebarTrigger />
|
||||
<h2 className="font-bold break-keep text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-none">Project Library</h2>
|
||||
{projects && <Label className="text-muted-foreground text-sm">(Found {projects.length} projects)</Label>}
|
||||
<ScrollFadingTitle className="flex flex-row items-center gap-2">
|
||||
<h2 className="font-bold break-keep text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-none">Project Library</h2>
|
||||
{projects && <Label className="text-muted-foreground text-sm">(Found {projects.length} projects)</Label>}
|
||||
</ScrollFadingTitle>
|
||||
</div>
|
||||
<div className="flex flex-col sticky top-safe bg-background gap-2 mt-2 pb-2 pt-2 p-5 w-[100% + 5 * var(--spacing)] z-10 -mx-5">
|
||||
<SidebarTriggerAdjustable>
|
||||
|
24
src/components/scroll-fading-title/ScrollFadingTitle.tsx
Normal file
24
src/components/scroll-fading-title/ScrollFadingTitle.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { ComponentProps, useEffect, useRef, useState } from "react";
|
||||
|
||||
const easeFade = (x: number) => (
|
||||
x === 0 ? 0 : Math.pow(2, 10 * x - 10)
|
||||
);
|
||||
|
||||
export const ScrollFadingTitle = (props: ComponentProps<"div">) => {
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (!elementRef.current) return;
|
||||
const opacity = easeFade(1 - (window.scrollY / ( window.innerHeight / 20)));
|
||||
elementRef.current.style.opacity = `${opacity}`;
|
||||
};
|
||||
|
||||
handleScroll();
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, [elementRef]);
|
||||
|
||||
return <div style={{opacity: 1}} ref={elementRef} {...props}/>
|
||||
};
|
3
src/components/scroll-fading-title/index.ts
Normal file
3
src/components/scroll-fading-title/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { ScrollFadingTitle } from "./ScrollFadingTitle";
|
||||
|
||||
export default ScrollFadingTitle;
|
@ -4,43 +4,32 @@ import { cn } from "@/lib/utils";
|
||||
import { ComponentProps, useEffect, useState } from "react";
|
||||
|
||||
const easeSlide = (x: number) => (
|
||||
x
|
||||
1 - Math.pow(1 - x, 3)
|
||||
);
|
||||
|
||||
export const SidebarTriggerAdjustable = (props: ComponentProps<"div">) => {
|
||||
const [scrollY, setScrollY] = useState(0);
|
||||
const [windowHeight, setWindowHeight] = useState(1);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
let ticking = false;
|
||||
|
||||
useEffect(() => {
|
||||
const triggerElement = document.querySelector('div[data-sidebar-trigger="true"]');
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
setScrollY(window.scrollY);
|
||||
ticking = false;
|
||||
});
|
||||
if (!triggerElement) {
|
||||
console.log("triggerElement is null");
|
||||
return;
|
||||
}
|
||||
const triggerDiv = triggerElement as HTMLDivElement;
|
||||
const slideAmount = easeSlide(Math.max(0, Math.min(1, window.scrollY / (window.innerHeight / 20))));
|
||||
triggerDiv.style.marginLeft = `calc(var(--spacing) * ${12 * slideAmount})`;
|
||||
triggerDiv.style.paddingTop = `calc(var(--spacing) * ${(isMobile ? 1 : 3) * slideAmount})`;
|
||||
};
|
||||
const handleResize = () => setWindowHeight(window.innerHeight);
|
||||
|
||||
setWindowHeight(window.innerHeight);
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
}, [isMobile]);
|
||||
|
||||
let slideAmount = Math.max(0, Math.min(1, scrollY / (windowHeight / 20)));
|
||||
slideAmount = easeSlide(slideAmount);
|
||||
|
||||
return <div {...props} style={{
|
||||
marginLeft: `calc(var(--spacing) * ${12 * slideAmount})`,
|
||||
marginTop: `calc(var(--spacing) * ${(isMobile ? 1 : 3) * slideAmount})`
|
||||
}}></div>;
|
||||
return <div data-sidebar-trigger="true" {...props}></div>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user