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 framer-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 "framer-motion";
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 |
Get beautiful, hand-crafted templates and components with Aceternity UI Pro
Professional, beautiful and elegant templates for your business. Get the best component packs and templates with Aceternity UI Pro.
The work that Manu did laid the foundation of online education that we provide today. The website he built for us is used by thousands of students every day. He took the requirements and built the ...
Jagvinder Kour
Chairperson at Golden Bells Academy