fix: use named createImageUrlBuilder from @sanity/image-url, make background parallax vibrant and clearly visible
This commit is contained in:
1 parent
a38e3b3e98
commit
408f968e96
2 files changed
+80
-63
No files matched your search
@@ -3,101 +3,118 @@ import { motion, useScroll, useTransform, useSpring } from 'framer-motion';
|
||||
|
||||
export default function Background() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [isLowTierOrMobile, setIsLowTierOrMobile] = useState(false);
|
||||
const [reducedMotion, setReducedMotion] = useState(false);
|
||||
|
||||
// Mouse spring for smooth desktop lighting tracking
|
||||
const mouseX = useSpring(0, { stiffness: 40, damping: 25 });
|
||||
const mouseY = useSpring(0, { stiffness: 40, damping: 25 });
|
||||
// Mouse spring for smooth desktop ambient lighting tracking
|
||||
const mouseX = useSpring(0, { stiffness: 50, damping: 28 });
|
||||
const mouseY = useSpring(0, { stiffness: 50, damping: 28 });
|
||||
|
||||
// Window scroll progress
|
||||
// Native window scroll tracking
|
||||
const { scrollY } = useScroll();
|
||||
|
||||
// Decoupled scroll parallax for each ambient layer
|
||||
const gridY = useTransform(scrollY, [0, 3000], [0, 90]);
|
||||
const orb1ScrollY = useTransform(scrollY, [0, 3000], [0, 260]);
|
||||
const orb1ScrollX = useTransform(scrollY, [0, 3000], [0, 80]);
|
||||
// Distinct parallax movement across scroll depths
|
||||
// Layer 1: Dot Matrix Grid (drifts downward with scroll)
|
||||
const gridY = useTransform(scrollY, [0, 2000], [0, 140]);
|
||||
|
||||
const orb2ScrollY = useTransform(scrollY, [0, 3000], [0, -280]);
|
||||
const orb2ScrollX = useTransform(scrollY, [0, 3000], [0, -70]);
|
||||
// Layer 2: Primary Indigo-Blue Orb (moves down and right)
|
||||
const orb1ScrollY = useTransform(scrollY, [0, 2000], [0, 380]);
|
||||
const orb1ScrollX = useTransform(scrollY, [0, 2000], [0, 120]);
|
||||
|
||||
const orb3ScrollY = useTransform(scrollY, [0, 3000], [0, -160]);
|
||||
// Layer 3: Secondary Violet-Cyan Orb (moves up and counter-left)
|
||||
const orb2ScrollY = useTransform(scrollY, [0, 2000], [0, -320]);
|
||||
const orb2ScrollX = useTransform(scrollY, [0, 2000], [0, -100]);
|
||||
|
||||
// Layer 4: Emerald-Teal Accent Glow (floats diagonally)
|
||||
const orb3ScrollY = useTransform(scrollY, [0, 2000], [0, -220]);
|
||||
const orb3ScrollX = useTransform(scrollY, [0, 2000], [0, 80]);
|
||||
|
||||
// Layer 5: Floating Geometric Depth Rings (distinct, noticeable parallax markers)
|
||||
const ring1Y = useTransform(scrollY, [0, 2000], [0, 450]);
|
||||
const ring2Y = useTransform(scrollY, [0, 2000], [0, -280]);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
|
||||
const isCoarse = window.matchMedia('(pointer: coarse)').matches;
|
||||
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
const isLowTier = document.documentElement.dataset.tier === 'low';
|
||||
|
||||
if (isCoarse || prefersReduced || isLowTier) {
|
||||
setIsLowTierOrMobile(true);
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
setReducedMotion(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
const centerX = window.innerWidth / 2;
|
||||
const centerY = window.innerHeight / 2;
|
||||
// Gentle offset (max ~35px)
|
||||
mouseX.set((e.clientX - centerX) * 0.04);
|
||||
mouseY.set((e.clientY - centerY) * 0.04);
|
||||
// Responsive mouse parallax offset (up to ~55px)
|
||||
mouseX.set((e.clientX - centerX) * 0.06);
|
||||
mouseY.set((e.clientY - centerY) * 0.06);
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove, { passive: true });
|
||||
return () => window.removeEventListener('mousemove', handleMouseMove);
|
||||
}, [mouseX, mouseY]);
|
||||
|
||||
// Static fallback during SSR or for low-end mobile/reduced-motion users
|
||||
if (!mounted || isLowTierOrMobile) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-0 pointer-events-none overflow-hidden" aria-hidden="true">
|
||||
{/* Subtle Dot Matrix Grid */}
|
||||
<div className="absolute inset-0 bg-[radial-gradient(hsla(var(--foreground)/0.12)_1px,transparent_1px)] [background-size:24px_24px] [mask-image:radial-gradient(ellipse_80%_80%_at_50%_50%,#000_60%,transparent_100%)] opacity-40" />
|
||||
|
||||
{/* Gentle Ambient Corner Glows (Static) */}
|
||||
<div className="absolute -top-40 -left-40 w-96 h-96 rounded-full bg-primary-custom/8 blur-[120px]" />
|
||||
<div className="absolute top-1/3 -right-40 w-96 h-96 rounded-full bg-secondary-custom/6 blur-[140px]" />
|
||||
<div className="absolute -bottom-40 left-1/4 w-96 h-96 rounded-full bg-primary-custom/6 blur-[120px]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-0 pointer-events-none overflow-hidden" aria-hidden="true">
|
||||
{/* ── Layer 1: Parallax Dot Matrix Grid ── */}
|
||||
<div className="fixed inset-0 z-0 pointer-events-none overflow-hidden select-none" aria-hidden="true">
|
||||
{/* ─── Layer 1: Visible Parallax Dot Matrix Grid ─── */}
|
||||
<motion.div
|
||||
style={{ y: gridY }}
|
||||
className="absolute -top-24 -bottom-24 inset-x-0 bg-[radial-gradient(hsla(var(--foreground)/0.12)_1px,transparent_1px)] [background-size:24px_24px] [mask-image:radial-gradient(ellipse_80%_80%_at_50%_50%,#000_60%,transparent_100%)] opacity-40 will-change-transform"
|
||||
style={reducedMotion ? {} : { y: gridY }}
|
||||
className="absolute -top-32 -bottom-32 inset-x-0 bg-[radial-gradient(hsla(var(--foreground)/0.18)_1.2px,transparent_1.2px)] [background-size:28px_28px] [mask-image:radial-gradient(ellipse_85%_85%_at_50%_50%,#000_65%,transparent_100%)] opacity-60 dark:opacity-45 will-change-transform"
|
||||
/>
|
||||
|
||||
{/* ── Layer 2: Top-Left Ambient Orb (Drifts down + tracks cursor) ── */}
|
||||
{/* ─── Layer 2: Vibrant Indigo-Blue Gradient Orb (Top-Left) ─── */}
|
||||
<motion.div
|
||||
style={{
|
||||
x: orb1ScrollX,
|
||||
y: orb1ScrollY,
|
||||
translateX: mouseX,
|
||||
translateY: mouseY,
|
||||
}}
|
||||
className="absolute -top-36 -left-36 w-[420px] h-[420px] rounded-full bg-primary-custom/9 blur-[130px] will-change-transform"
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: {
|
||||
x: orb1ScrollX,
|
||||
y: orb1ScrollY,
|
||||
translateX: mouseX,
|
||||
translateY: mouseY,
|
||||
}
|
||||
}
|
||||
className="absolute -top-40 -left-40 w-[520px] sm:w-[620px] h-[520px] sm:h-[620px] rounded-full bg-gradient-to-br from-blue-600/25 via-indigo-500/20 to-purple-600/15 dark:from-blue-500/25 dark:via-indigo-500/25 dark:to-cyan-400/20 blur-[85px] will-change-transform"
|
||||
/>
|
||||
|
||||
{/* ── Layer 3: Middle-Right Secondary Glow (Drifts up opposite + inverse cursor) ── */}
|
||||
{/* ─── Layer 3: Violet-Pink-Cyan Secondary Orb (Center-Right) ─── */}
|
||||
<motion.div
|
||||
style={{
|
||||
x: orb2ScrollX,
|
||||
y: orb2ScrollY,
|
||||
translateX: useTransform(mouseX, (v) => -v * 1.2),
|
||||
translateY: useTransform(mouseY, (v) => -v * 1.2),
|
||||
}}
|
||||
className="absolute top-1/3 -right-36 w-[440px] h-[440px] rounded-full bg-secondary-custom/7 blur-[145px] will-change-transform"
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: {
|
||||
x: orb2ScrollX,
|
||||
y: orb2ScrollY,
|
||||
translateX: useTransform(mouseX, (v) => -v * 1.3),
|
||||
translateY: useTransform(mouseY, (v) => -v * 1.3),
|
||||
}
|
||||
}
|
||||
className="absolute top-1/3 -right-36 w-[500px] sm:w-[580px] h-[500px] sm:h-[580px] rounded-full bg-gradient-to-bl from-purple-600/25 via-fuchsia-500/20 to-cyan-500/15 dark:from-purple-500/25 dark:via-pink-500/20 dark:to-indigo-500/20 blur-[90px] will-change-transform"
|
||||
/>
|
||||
|
||||
{/* ── Layer 4: Lower Primary Glow (Slow floating depth) ── */}
|
||||
{/* ─── Layer 4: Emerald-Teal Radiant Orb (Lower Section) ─── */}
|
||||
<motion.div
|
||||
style={{
|
||||
y: orb3ScrollY,
|
||||
translateX: useTransform(mouseX, (v) => v * 0.7),
|
||||
}}
|
||||
className="absolute -bottom-36 left-1/4 w-[400px] h-[400px] rounded-full bg-primary-custom/7 blur-[130px] will-change-transform"
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: {
|
||||
x: orb3ScrollX,
|
||||
y: orb3ScrollY,
|
||||
translateX: useTransform(mouseX, (v) => v * 0.9),
|
||||
}
|
||||
}
|
||||
className="absolute -bottom-48 left-1/4 w-[480px] sm:w-[560px] h-[480px] sm:h-[560px] rounded-full bg-gradient-to-tr from-emerald-500/20 via-teal-500/20 to-blue-500/15 dark:from-emerald-400/20 dark:via-cyan-500/20 dark:to-blue-500/20 blur-[85px] will-change-transform"
|
||||
/>
|
||||
|
||||
{/* ─── Layer 5: High-Visibility Parallax Depth Markers ─── */}
|
||||
{/* Subtle floating ambient rings that visibly move faster/slower than cards */}
|
||||
<motion.div
|
||||
style={reducedMotion ? {} : { y: ring1Y }}
|
||||
className="hidden md:block absolute top-1/4 left-[8%] w-72 h-72 rounded-full border border-primary-custom/20 dark:border-primary-custom/25 [mask-image:radial-gradient(circle_at_center,transparent_45%,#000_65%)] will-change-transform"
|
||||
/>
|
||||
<motion.div
|
||||
style={reducedMotion ? {} : { y: ring2Y }}
|
||||
className="hidden md:block absolute top-2/3 right-[10%] w-96 h-96 rounded-full border border-secondary-custom/20 dark:border-secondary-custom/25 [mask-image:radial-gradient(circle_at_center,transparent_45%,#000_65%)] will-change-transform"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { createClient } from '@sanity/client';
|
||||
import imageUrlBuilder from '@sanity/image-url';
|
||||
import { createImageUrlBuilder } from '@sanity/image-url';
|
||||
|
||||
const projectId = import.meta.env.PUBLIC_SANITY_PROJECT_ID ||
|
||||
(typeof process !== 'undefined' ? (process.env.PUBLIC_SANITY_PROJECT_ID || process.env.SANITY_PROJECT_ID) : undefined);
|
||||
@@ -23,7 +23,7 @@ export const sanityClient = projectId
|
||||
})
|
||||
: null;
|
||||
|
||||
const builder = sanityClient ? imageUrlBuilder(sanityClient) : null;
|
||||
const builder = sanityClient ? createImageUrlBuilder(sanityClient) : null;
|
||||
|
||||
export function urlFor(source: any) {
|
||||
return builder ? builder.image(source).url() : '';
|
||||
|
||||
Reference in new issue
Block a user