-
Notifications
You must be signed in to change notification settings - Fork 30
Add reusable MouseParallax component to Hero for layered visual depth #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import { Globe } from "@/components/ui/globe"; | |
| import { OrbitSatellites } from "@/components/ui/OrbitSatellites"; | ||
| import { ShimmerButton } from "@/components/ui/shimmer-button"; | ||
| import { Link } from "react-router-dom"; | ||
| import { MouseParallax } from "./MouseParallax"; | ||
|
|
||
| const TELEMETRY = [ | ||
| { label: "OBJECTS TRACKED", value: "34,900+" }, | ||
|
|
@@ -25,16 +26,23 @@ const fadeUp = { | |
| export function Hero() { | ||
| return ( | ||
| <section className="relative flex min-h-screen w-full items-center overflow-hidden bg-[radial-gradient(120%_90%_at_50%_10%,#0B111F_0%,#05070C_60%)] font-[Inter]"> | ||
| <Particles className="absolute inset-0" quantity={220} /> | ||
| <MouseParallax className="absolute inset-0" strength={20}> | ||
| <Particles quantity={220} /> | ||
| </MouseParallax> | ||
|
|
||
|
|
||
| <div | ||
| className="absolute left-1/2 top-[32%] h-[240px] w-[240px] max-w-[85vw] -translate-x-1/2 -translate-y-1/2 opacity-40 | ||
| sm:left-auto sm:right-[3%] sm:top-1/2 sm:h-[380px] sm:w-[380px] sm:translate-x-0 sm:-translate-y-1/2 sm:opacity-70 | ||
| lg:right-[5%] lg:h-[520px] lg:w-[520px] lg:opacity-90" | ||
| > | ||
| <OrbitSatellites /> | ||
| <div className="absolute inset-0 flex items-center justify-center"> | ||
| <MouseParallax strength={10}> | ||
| <OrbitSatellites /> | ||
| </MouseParallax> | ||
|
Comment on lines
+39
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The satellite parallax wrapper has no sizing or positioning class, while Severity Level: Major
|
||
|
|
||
| <MouseParallax strength={20} className="absolute inset-0 flex items-center justify-center"> | ||
| <Globe className="h-max-[480px] w-max-[480px]" /> | ||
| </div> | ||
| </MouseParallax> | ||
|
Comment on lines
+29
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π Major | ποΈ Heavy lift Use one pointer-tracking region for all overlapping layers. The absolute Track the pointer on a common Hero region. Share the resulting motion values with each layer, then apply each layer strength independently. π€ Prompt for AI Agents |
||
| </div> | ||
|
|
||
| <div className="pointer-events-none absolute inset-0 bg-[linear-gradient(90deg,#05070C_0%,rgba(5,7,12,0.85)_38%,rgba(5,7,12,0.25)_62%,transparent_85%)] sm:bg-[linear-gradient(90deg,#05070C_0%,rgba(5,7,12,0.85)_38%,rgba(5,7,12,0.25)_62%,transparent_85%)]" /> | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {useRef, type ReactNode} from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {motion, useMotionValue, useSpring} from "framer-motion" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface MouseParallaxProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| children: ReactNode; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| strength?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| springConfig?: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stiffness?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| damping?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mass?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function MouseParallax({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| children, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| strength = 20, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| springConfig = {stiffness: 150, damping: 15, mass: 0.1}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: MouseParallaxProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ref = useRef<HTMLDivElement>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const x = useMotionValue(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const y = useMotionValue(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const springX = useSpring(x, springConfig); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const springY = useSpring(y, springConfig); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rect = ref.current?.getBoundingClientRect(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!rect) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const centerX = rect.left + rect.width / 2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const centerY = rect.top + rect.height / 2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const offsetX = (e.clientX - centerX) / (rect.width / 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const offsetY = (e.clientY - centerY) / (rect.height / 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| x.set(offsetX * strength); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| y.set(offsetY * strength); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π‘ Minor | β‘ Quick win Guard zero-sized containers before calculating offsets.
Proposed fix const rect = ref.current?.getBoundingClientRect();
if (!rect) return;
+ if (rect.width <= 0 || rect.height <= 0) {
+ x.set(0);
+ y.set(0);
+ return;
+ }
const centerX = rect.left + rect.width / 2;π Committable suggestion
Suggested change
π€ Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleMouseLeave = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| x.set(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| y.set(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <motion.div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ref={ref} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={className} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMouseMove={handleMouseMove} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMouseLeave={handleMouseLeave} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{x: springX, y: springY}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {children} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </motion.div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The new parallax effects are unconditionally enabled, including spring-driven transforms for users whose system requests reduced motion. The existing reduced-motion handling only affects CSS animations and cannot disable these Framer Motion updates, so the hero introduces pointer-driven motion despite that accessibility preference. [possible bug]
Severity Level: Majorβ οΈ
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent π€