Lens
A lens component to zoom into images, videos, or practically anything.
Apple Vision Pro
The all new apple vision pro was the best thing that happened around 8 months ago, not anymore.
Installation
Install dependencies
npm i motion clsx tailwind-merge
Add util file
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Copy the source code
components/ui/lens.tsx
"use client";
import React, { useRef, useState } from "react";
import { AnimatePresence, motion } from "motion/react";
interface LensProps {
children: React.ReactNode;
zoomFactor?: number;
lensSize?: number;
position?: {
x: number;
y: number;
};
isStatic?: boolean;
isFocusing?: () => void;
hovering?: boolean;
setHovering?: (hovering: boolean) => void;
}
export const Lens: React.FC<LensProps> = ({
children,
zoomFactor = 1.5,
lensSize = 170,
isStatic = false,
position = { x: 200, y: 150 },
hovering,
setHovering,
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [localIsHovering, setLocalIsHovering] = useState(false);
const isHovering = hovering !== undefined ? hovering : localIsHovering;
const setIsHovering = setHovering || setLocalIsHovering;
// const [isHovering, setIsHovering] = useState(false);
const [mousePosition, setMousePosition] = useState({ x: 100, y: 100 });
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
setMousePosition({ x, y });
};
return (
<div
ref={containerRef}
className="relative overflow-hidden rounded-lg z-20"
onMouseEnter={() => {
setIsHovering(true);
}}
onMouseLeave={() => setIsHovering(false)}
onMouseMove={handleMouseMove}
>
{children}
{isStatic ? (
<div>
<motion.div
initial={{ opacity: 0, scale: 0.58 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="absolute inset-0 overflow-hidden"
style={{
maskImage: `radial-gradient(circle ${lensSize / 2}px at ${
position.x
}px ${position.y}px, black 100%, transparent 100%)`,
WebkitMaskImage: `radial-gradient(circle ${lensSize / 2}px at ${
position.x
}px ${position.y}px, black 100%, transparent 100%)`,
transformOrigin: `${position.x}px ${position.y}px`,
}}
>
<div
className="absolute inset-0"
style={{
transform: `scale(${zoomFactor})`,
transformOrigin: `${position.x}px ${position.y}px`,
}}
>
{children}
</div>
</motion.div>
</div>
) : (
<AnimatePresence>
{isHovering && (
<div>
<motion.div
initial={{ opacity: 0, scale: 0.58 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="absolute inset-0 overflow-hidden"
style={{
maskImage: `radial-gradient(circle ${lensSize / 2}px at ${
mousePosition.x
}px ${mousePosition.y}px, black 100%, transparent 100%)`,
WebkitMaskImage: `radial-gradient(circle ${
lensSize / 2
}px at ${mousePosition.x}px ${
mousePosition.y
}px, black 100%, transparent 100%)`,
transformOrigin: `${mousePosition.x}px ${mousePosition.y}px`,
zIndex: 50,
}}
>
<div
className="absolute inset-0"
style={{
transform: `scale(${zoomFactor})`,
transformOrigin: `${mousePosition.x}px ${mousePosition.y}px`,
}}
>
{children}
</div>
</motion.div>
</div>
)}
</AnimatePresence>
)}
</div>
);
};
Examples
Basic with Animation
Apple Vision Pro
The all new apple vision pro was the best thing that happened around 8 months ago, not anymore.
Static
Apple Vision Pro
The all new apple vision pro was the best thing that happened around 8 months ago, not anymore.
Lens on a React Component
Apple Vision Pro
The all new apple vision pro was the best thing that happened around 8 months ago, not anymore.
Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | Required | The content to be displayed inside the lens |
zoomFactor | number | 1.5 | The magnification factor for the lens |
lensSize | number | 170 | The diameter of the lens in pixels |
position | { x: number, y: number } | { x: 200, y: 150 } | The static position of the lens (when isStatic is true) |
isStatic | boolean | false | If true, the lens stays in a fixed position; if false, it follows the mouse |
isFocusing | () => void | - | Callback function when the lens is focusing (not used in current implementation) |
hovering | boolean | - | External control for the hover state |
setHovering | (hovering: boolean) => void | - | External setter for the hover state |
Build websites faster and 10x better than your competitors with Aceternity UI Pro
With the best in class components and templates, stand out from the crowd and get more attention to your website. Trusted by founders and entrepreneurs from all over the world.
I'd highly recommend working with Manu on a site redesign. We came to Manu with a basic website shell. Manu quickly took our rough concept and transformed it into a polished, user-friendly website....
Ray Thai
Head of Product at Fireworks