A highly performant, customizable Angular image magnifier directive with keyboard modifier support, smart positioning, mobile optimization, and smooth GPU-accelerated animations.
✨ Performance-Optimized
- GPU-accelerated positioning using
transform: translate() - Passive event listeners for better scroll performance
- Selective keyboard listener attachment (only when needed)
- CSS containment for isolated rendering
- RequestAnimationFrame batching for 60fps smooth movement
🎯 Smart Positioning
- Intelligent "auto" positioning that adapts to viewport constraints
- 9 manual positioning modes:
right,left,top,bottom,bottom-right,bottom-left,top-right,top-left,auto - Automatic avoidance of the source image
- Padding customization for screen edge spacing
📱 Mobile Optimized
- Separate configuration for mobile devices
- Mobile-specific size, ratio, and positioning options
- Touch-friendly interactions
- Responsive breakpoint detection (768px)
⌨️ Keyboard Modifier Support
- Optional requirement for
shift,ctrl, oraltkeys - Smart hint tooltips with auto-generated text
- Seamless modifier state tracking
- Mobile devices ignore modifier requirements
🎨 Customizable
- Configurable magnifier size and aspect ratio
- Custom border radius (including fully rounded circles)
- Smooth transition animations
- Custom hint text or disable tooltips entirely
npm install @sezsahin/ngx-image-magnifierImport the directive and apply it to any image:
import { Component } from "@angular/core";
import { ImageMagnifierDirective } from "@sezsahin/ngx-image-magnifier";
@Component({
selector: "app-gallery",
standalone: true,
imports: [ImageMagnifierDirective],
template: ` <img appImageMagnifier src="photo.jpg" alt="Gallery image" /> `,
})
export class GalleryComponent {}<img appImageMagnifier src="product.jpg" alt="Product" [magnifierSize]="300" />By default, the magnifier shows the image at actual size. To enable zoom:
<img
appImageMagnifier
src="product.jpg"
alt="Product"
[magnifierSize]="300"
[zoom]="2"
/>Require holding Ctrl to magnify:
<img
appImageMagnifier
src="detailed-map.jpg"
alt="Map"
[magnifierSize]="400"
requireKeyModifier="ctrl"
/>Mobile users can view the magnifier without holding the modifier.
<img
appImageMagnifier
src="artwork.jpg"
alt="Fine Art"
[magnifierSize]="350"
[hintText]="'Click to zoom in'"
/><img
appImageMagnifier
src="thumbnail.jpg"
alt="Thumbnail"
[magnifierSize]="250"
[showHint]="false"
/><img
appImageMagnifier
src="responsive-image.jpg"
alt="Responsive"
[magnifierSize]="400"
[magnifierSizeMobile]="250"
[magnifierRatio]="'16/9'"
[magnifierRatioMobile]="'1/1'"
position="auto"
[positionMobile]="'bottom'"
/><img
appImageMagnifier
src="profile.jpg"
alt="Profile"
[magnifierSize]="300"
[rounded]="true"
/><img
appImageMagnifier
src="centered-image.jpg"
alt="Centered"
position="right"
[animateTransition]="true"
[padding]="30"
/><!-- Zoom follows cursor -->
<img appImageMagnifier src="map.jpg" alt="Map" [zoom]="3" zoomFocus="cursor" />
<!-- Zoom with custom focus point -->
<img
appImageMagnifier
src="product.jpg"
alt="Product"
[zoom]="2.5"
zoomFocus="custom"
[zoomFocusX]="75"
[zoomFocusY]="25"
/>
<!-- Zoom focused on top-right corner -->
<img
appImageMagnifier
src="detail.jpg"
alt="Detail"
[zoom]="2"
zoomFocus="top-right"
/>| Property | Type | Default | Description |
|---|---|---|---|
magnifierSize |
number |
300 |
Width of magnifier window in pixels |
magnifierSizeMobile |
number | null |
null |
Mobile-specific magnifier size (60% of desktop if not set) |
magnifierRatio |
string | null |
null |
Aspect ratio as string, e.g., '1/1' or '16/9'. If null, uses image's natural ratio |
magnifierRatioMobile |
string | null |
null |
Mobile-specific aspect ratio |
position |
'auto' | 'right' | 'left' | 'top' | 'bottom' | 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' |
'auto' |
Positioning strategy for magnifier placement |
positionMobile |
'auto' | 'right' | 'left' | 'top' | 'bottom' | 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | null |
null |
Mobile-specific positioning (uses desktop position if null) |
animateTransition |
boolean |
true |
Enable smooth 320ms transitions when moving magnifier |
rounded |
boolean |
false |
Apply circular border-radius (50%) to magnifier |
padding |
number | null |
null |
Padding from screen edges. Auto-calculated (5% of size + 20px min) if null |
requireKeyModifier |
'shift' | 'ctrl' | 'alt' | null |
null |
Require holding modifier key to display magnifier. Ignored on mobile |
showHint |
boolean |
true |
Show tooltip hint on first hover (only when modifier required) |
hintText |
string | null |
null |
Custom hint text. Auto-generated based on modifier if null |
zoom |
number | null |
null |
Zoom factor for magnification (e.g., 2 for 2x zoom). null shows image at actual size |
zoomFocus |
'center' | 'cursor' | 'top-left' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right' | 'custom' |
'center' |
Focus point for zoom. 'cursor' follows mouse, 'center' centers image, or use specific position |
zoomFocusX |
number | null |
null |
Custom X focus percentage (0-100) when zoomFocus='custom' |
zoomFocusY |
number | null |
null |
Custom Y focus percentage (0-100) when zoomFocus='custom' |
Intelligently places the magnifier to fit the viewport while avoiding the source image. Adapts based on available space.
Places magnifier to the right or left of cursor, vertically centered.
Places magnifier above or below cursor, horizontally centered.
Places magnifier diagonally (right/left with slight downward offset).
Places magnifier diagonally (right/left with slight upward offset).
- Chrome/Edge: Latest 2 versions
- Firefox: Latest 2 versions
- Safari: Latest 2 versions
- Mobile browsers: iOS Safari 12+, Chrome/Firefox on Android
SSR Compatible: The directive uses isPlatformBrowser checks for universal rendering support.
The directive is optimized for 60fps smooth interactions:
- GPU Acceleration: Uses
transform: translate()instead of layout-affecting properties - Passive Listeners: Mouse events don't block scrolling
- Event Batching: Position updates batched with
requestAnimationFrame - Lazy Listeners: Keyboard listeners only attached when
requireKeyModifieris set - CSS Containment: Rendering isolated from page layout
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details
- Aligned package entry points/exports to the actual ng-packagr output paths so module resolution works correctly.. again.
- Improved zoom focus behavior to clamp background position within image boundaries
- Prevents blank areas from showing when zoomed in with any focus mode (cursor, custom coordinates, or predefined positions)
- Ensures focus point aligns as closely as possible to requested position without exceeding image edges
- High zoom values now properly display corresponding image areas while keeping everything in bounds
- Aligned package entry points/exports to the actual ng-packagr output paths so module resolution works correctly.
Fixed
- Fixed npm installation issues in Angular packages
- Added missing build scripts (
buildandbuild:watch) to package.json - Generated proper dist folder with compiled modules, type definitions, and manifests
- Ensured library exports are correctly configured for npm distribution
- Added missing build scripts (
Changed
- Changed default zoom from
2tonull(no zoom by default) - Images now display at actual size unless zoom property is explicitly set
- More dynamic and controllable zoom behavior
Changed
- Updated docs.
Changed
- Updated GitHub repository URL in package.json to the correct link.
- Updated homepage URL in package.json to the correct link.
Changed
- Magnifier now uses zoom scaling when no ratio is set.
Changed
- Renamed positioning options:
right-below→bottom-rightleft-below→bottom-leftright-above→top-rightleft-above→top-left
- Documentation and API updated to reflect new names.
Added
- New positioning options:
right-aboveandleft-abovefor bothpositionandpositionMobileinputs. - Documentation updated to reflect new options.
Fixed
- Fixed TypeScript type compatibility for timeout handling
- Changed timeout type from
numbertoReturnType<typeof setTimeout>for proper cross-environment support - Ensures compatibility with both browser and Node.js environments
- Changed timeout type from
Improved
- Comprehensive memory leak prevention enhancements
- Fixed orphaned event listeners that weren't being cleaned up on component destruction
- Properly track and clear all timeouts (hint, fadeout, and removal) with proper
ReturnType<typeof setTimeout>typing - Store media query listener reference for proper cleanup
- Nullify all handler references to enable garbage collection
- Improved
ngOnDestroy()with complete cleanup of keyboard, mouse, and media query listeners - Fixed TypeScript type compatibility for timeout handling
Fixed
- Fixed tooltip glitching issue when hovering over images and leaving too quickly
- Added guard to prevent duplicate hint instances
- Improved hint cleanup logic to prevent visual glitches
- Better lifecycle management for hint tooltips
Fixed
- Fixed hint tooltip showing on mobile devices
- Added mobile device check to prevent tooltips on touch devices
- Full-featured image magnifier directive
- GPU-accelerated smooth positioning
- Keyboard modifier support with smart hints
- Mobile optimization with responsive sizing
- Comprehensive customization options
- Production-ready performance optimizations