rm shine + perf
This commit is contained in:
parent
0db321ab85
commit
ddffa2bbdc
4 changed files with 39 additions and 94 deletions
|
|
@ -29,7 +29,7 @@ export function Navbar() {
|
|||
const [logoHovered, setLogoHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<header data-tour="navbar" className="top-0 z-40 h-16 w-full">
|
||||
<header className="top-0 z-40 h-16 w-full">
|
||||
<div className="mx-auto flex h-full max-w-7xl items-center justify-between px-6">
|
||||
{/* Left: logo */}
|
||||
<div
|
||||
|
|
@ -63,7 +63,10 @@ export function Navbar() {
|
|||
</div>
|
||||
|
||||
{/* Center: pill nav */}
|
||||
<nav className="flex items-center rounded-full border border-border bg-card p-1 ring-1 ring-foreground/5">
|
||||
<nav
|
||||
data-tour="navbar"
|
||||
className="flex items-center rounded-full border border-border bg-card p-1 ring-1 ring-foreground/5"
|
||||
>
|
||||
{NAV_ITEMS.map((item) => {
|
||||
const active = pathname === item.href;
|
||||
if (!item.enabled) {
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface ShineBorderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Width of the border in pixels
|
||||
* @default 1
|
||||
*/
|
||||
borderWidth?: number
|
||||
/**
|
||||
* Duration of the animation in seconds
|
||||
* @default 14
|
||||
*/
|
||||
duration?: number
|
||||
/**
|
||||
* Color of the border, can be a single color or an array of colors
|
||||
* @default "#000000"
|
||||
*/
|
||||
shineColor?: string | string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Shine Border
|
||||
*
|
||||
* An animated background border effect component with configurable properties.
|
||||
*/
|
||||
export function ShineBorder({
|
||||
borderWidth = 1,
|
||||
duration = 14,
|
||||
shineColor = "#000000",
|
||||
className,
|
||||
style,
|
||||
...props
|
||||
}: ShineBorderProps) {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--border-width": `${borderWidth}px`,
|
||||
"--duration": `${duration}s`,
|
||||
backgroundImage: `radial-gradient(transparent,transparent, ${
|
||||
Array.isArray(shineColor) ? shineColor.join(",") : shineColor
|
||||
},transparent,transparent)`,
|
||||
backgroundSize: "300% 300%",
|
||||
mask: `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
|
||||
WebkitMask: `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
|
||||
WebkitMaskComposite: "xor",
|
||||
maskComposite: "exclude",
|
||||
padding: "var(--border-width)",
|
||||
...style,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn(
|
||||
"motion-safe:animate-shine pointer-events-none absolute inset-0 size-full rounded-[inherit] will-change-[background-position]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { ShineBorder } from "@/components/ui/shine-border";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import {
|
||||
|
|
@ -174,6 +173,8 @@ export function GuidedTour({
|
|||
});
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
const closeLockRef = useRef(false);
|
||||
const rafRef = useRef<number | null>(null);
|
||||
const lastRectRef = useRef<Rect | null>(null);
|
||||
|
||||
const step = steps[idx] ?? null;
|
||||
const total = steps.length;
|
||||
|
|
@ -181,8 +182,9 @@ export function GuidedTour({
|
|||
|
||||
const spotlightRect = useMemo(() => {
|
||||
if (!targetRect || !vw || !vh) return null;
|
||||
return padded(targetRect, 14, vw, vh);
|
||||
}, [targetRect, vw, vh]);
|
||||
const pad = step?.target === "navbar" ? 8 : 14;
|
||||
return padded(targetRect, pad, vw, vh);
|
||||
}, [step?.target, targetRect, vw, vh]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
|
@ -215,23 +217,45 @@ export function GuidedTour({
|
|||
|
||||
let raf = 0;
|
||||
let t = 0;
|
||||
const update = () => {
|
||||
const read = () => {
|
||||
const r = el.getBoundingClientRect();
|
||||
setTargetRect(toRect(r));
|
||||
const next = toRect(r);
|
||||
const prev = lastRectRef.current;
|
||||
if (
|
||||
!prev ||
|
||||
Math.abs(prev.x - next.x) > 0.5 ||
|
||||
Math.abs(prev.y - next.y) > 0.5 ||
|
||||
Math.abs(prev.w - next.w) > 0.5 ||
|
||||
Math.abs(prev.h - next.h) > 0.5
|
||||
) {
|
||||
lastRectRef.current = next;
|
||||
setTargetRect(next);
|
||||
}
|
||||
};
|
||||
const schedule = () => {
|
||||
if (rafRef.current != null) return;
|
||||
rafRef.current = window.requestAnimationFrame(() => {
|
||||
rafRef.current = null;
|
||||
read();
|
||||
});
|
||||
};
|
||||
|
||||
raf = window.requestAnimationFrame(update);
|
||||
t = window.setTimeout(update, 240);
|
||||
raf = window.requestAnimationFrame(read);
|
||||
t = window.setTimeout(schedule, 240);
|
||||
|
||||
const ro = new ResizeObserver(() => update());
|
||||
const ro = new ResizeObserver(() => schedule());
|
||||
ro.observe(el);
|
||||
window.addEventListener("scroll", update, { capture: true, passive: true });
|
||||
window.addEventListener("scroll", schedule, { capture: true, passive: true });
|
||||
|
||||
return () => {
|
||||
window.cancelAnimationFrame(raf);
|
||||
window.clearTimeout(t);
|
||||
ro.disconnect();
|
||||
window.removeEventListener("scroll", update, true);
|
||||
window.removeEventListener("scroll", schedule, true);
|
||||
if (rafRef.current != null) {
|
||||
window.cancelAnimationFrame(rafRef.current);
|
||||
rafRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [open, step]);
|
||||
|
||||
|
|
@ -321,15 +345,6 @@ export function GuidedTour({
|
|||
fontFamily: "'Figtree Variable', ui-sans-serif, sans-serif",
|
||||
}}
|
||||
>
|
||||
<ShineBorder
|
||||
borderWidth={1}
|
||||
duration={12}
|
||||
shineColor={[
|
||||
"rgba(16,185,129,0.65)",
|
||||
"rgba(34,211,238,0.65)",
|
||||
"rgba(16,185,129,0.35)",
|
||||
]}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute z-10 size-3 rotate-45 rounded-[3px] bg-white/95 ring-1 ring-black/10",
|
||||
|
|
|
|||
|
|
@ -242,18 +242,6 @@
|
|||
background-position: calc(100% + var(--shiny-width)) 0;
|
||||
}
|
||||
}
|
||||
--animate-shine: shine var(--duration) infinite linear;
|
||||
@keyframes shine {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue