Lens
svelte-animations/lensFreeBlock
An interactive component that enables zooming into images, videos and other elements.
Install it
npx shadcn@latest add https://sv-animations.vercel.app/r/lens.jsonSource
1<script lang="ts">2 import { motion, AnimatePresence } from "motion-sv";3 import { cn } from "$UTILS$";4 import type { Snippet } from "svelte";56 interface Position {7 /** The x coordinate of the lens */8 x: number;9 /** The y coordinate of the lens */10 y: number;11 }1213 interface LensProps {14 children: Snippet;15 /** The zoom factor of the lens */16 zoomFactor?: number;17 /** The size of the lens */18 lensSize?: number;19 /** The position of the lens */20 position?: Position;21 /** The default position of the lens */22 defaultPosition?: Position;23 /** Whether the lens is static */24 isStatic?: boolean;25 /** The duration of the animation */26 duration?: number;27 /** The color of the lens */28 lensColor?: string;29 /** The aria label of the lens */30 ariaLabel?: string;31 class?: string;32 }3334 let {35 children,36 zoomFactor = 1.3,37 lensSize = 170,38 isStatic = false,39 position = { x: 0, y: 0 },40 defaultPosition,41 duration = 0.1,42 lensColor = "black",43 ariaLabel = "Zoom Area",44 class: className,45 }: LensProps = $props();4647 // Validation48 $effect(() => {49 if (zoomFactor < 1) {50 throw new Error("zoomFactor must be greater than 1");51 }52 if (lensSize < 0) {53 throw new Error("lensSize must be greater than 0");54 }55 });5657 let isHovering = $state(false);58 let mousePosition: Position = $derived(position);59 let containerRef: HTMLDivElement | null = $state(null);6061 const currentPosition = $derived.by(() => {62 if (isStatic) return position;63 if (defaultPosition && !isHovering) return defaultPosition;64 return mousePosition;65 });6667 const handleMouseMove = (e: MouseEvent) => {68 if (!containerRef) return;69 const rect = containerRef.getBoundingClientRect();70 mousePosition = {71 x: e.clientX - rect.left,72 y: e.clientY - rect.top,73 };74 };7576 const handleKeyDown = (e: KeyboardEvent) => {77 if (e.key === "Escape") isHovering = false;78 };7980 const maskImage = $derived(81 `radial-gradient(circle ${lensSize / 2}px at ${currentPosition.x}px ${currentPosition.y}px, ${lensColor} 100%, transparent 100%)`82 );8384 const transformOrigin = $derived(`${currentPosition.x}px ${currentPosition.y}px`);8586 const lensStyle = $derived({87 maskImage,88 WebkitMaskImage: maskImage,89 transformOrigin,90 zIndex: 50,91 });9293 const zoomStyleString = $derived(94 `transform: scale(${zoomFactor}); transform-origin: ${transformOrigin};`95 );96</script>9798<!-- svelte-ignore a11y_no_noninteractive_tabindex -->99<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->100<div101// … truncated
What it pulls in
npm packages
Files it writes
More from Svelte Animations
All 66 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Beamanimated-beam | Svelte Animations | Blocks | Free | 1 dep · 6 files | |
| Animated Circular Progress Baranimated-circular-progress-bar | Svelte Animations | Blocks | Free | no deps · 2 files | |
| Animated Gradient Textanimated-gradient-text | Svelte Animations | Blocks | Free | no deps · 2 files | |
| Animated Grid Patternanimated-grid-pattern | Svelte Animations | Blocks | Free | 1 dep · 2 files | |
| Animated Listanimated-list | Svelte Animations | Blocks | Free | 1 dep · 2 files | |
| Animated Shiny Textanimated-shiny-text | Svelte Animations | Blocks | Free | no deps | |
| Animated Theme Toggleranimated-theme-toggler | Svelte Animations | Blocks | Free | no deps · 2 files | |
| Arc Timelinearc-timeline | Svelte Animations | Blocks | Free | no deps · 3 files |
