('pinchZoom');
brightnessIn() {
this.pinchZoom()?.brightnessIn();
}
brightnessOut() {
this.pinchZoom()?.brightnessOut();
}
resetBrightness() {
this.pinchZoom()?.resetBrightness();
}
}
```
### Custom Controls with Plus/Minus Buttons
Build your own custom UI by disabling default controls and using the public methods with smart min/max behavior:
```typescript
import { Component, viewChild, signal, computed } from '@angular/core';
import { PinchZoomComponent } from '@brianpooe/ngx-pinch-zoom';
@Component({
selector: 'app-custom-controls',
standalone: true,
imports: [PinchZoomComponent],
template: `
Zoom: {{ zoomLevel() }} | Brightness: {{ brightnessLevel() }}
`,
})
export class CustomControlsComponent {
pinchZoom = viewChild('pinchZoom');
zoomLevel = signal(1);
brightnessLevel = signal(1.0);
// Min/max constants
private readonly MIN_ZOOM = 1;
private readonly MIN_BRIGHTNESS = 1.0;
private readonly MAX_BRIGHTNESS = 2.0;
// Computed signals for button states
isZoomAtMin = computed(() => this.zoomLevel() <= this.MIN_ZOOM);
isBrightnessAtMin = computed(() => this.brightnessLevel() <= this.MIN_BRIGHTNESS);
// Smart handlers with min/max and reset behavior
handleZoomIn() {
const pinch = this.pinchZoom();
if (!pinch) return;
const maxScale = pinch.maxScale();
if (this.zoomLevel() >= maxScale) {
// At max, reset to min
pinch.destroy();
} else {
pinch.zoomIn(0.5);
}
}
handleZoomOut() {
const pinch = this.pinchZoom();
if (!pinch || this.isZoomAtMin()) return;
pinch.zoomOut(0.5);
}
handleBrightnessIn() {
const pinch = this.pinchZoom();
if (!pinch) return;
if (this.brightnessLevel() >= this.MAX_BRIGHTNESS) {
// At max, reset to normal
pinch.resetBrightness();
} else {
pinch.brightnessIn();
}
}
handleBrightnessOut() {
const pinch = this.pinchZoom();
if (!pinch || this.isBrightnessAtMin()) return;
pinch.brightnessOut();
}
onZoomChange(scale: number) {
this.zoomLevel.set(scale);
}
onBrightnessChange(brightness: number) {
this.brightnessLevel.set(brightness);
}
}
```
**Smart Control Behavior:**
- **Minus buttons**: Disabled when at minimum value (initial state), prevent going below min
- **Plus buttons**: When at maximum value, clicking resets to minimum instead of being disabled
- This pattern provides intuitive UX - users can't accidentally go below min, and can easily reset from max
## Configuration Options
| Input | Type | Default | Description |
| ------------------------- | --------------------------------- | ----------------------- | ----------------------------------------- |
| `transitionDuration` | `number` | `200` | Animation duration in milliseconds |
| `doubleTap` | `boolean` | `true` | Enable double-tap to zoom |
| `doubleTapScale` | `number` | `2` | Scale factor for double-tap zoom |
| `autoZoomOut` | `boolean` | `false` | Automatically reset zoom after pinch |
| `limitZoom` | `number \| 'original image size'` | `'original image size'` | Maximum zoom level |
| `minScale` | `number` | `0` | Minimum allowed scale |
| `disabled` | `boolean` | `false` | Disable all zoom functionality |
| `disablePan` | `boolean` | `false` | Disable panning with one finger |
| `disableZoomControl` | `'disable' \| 'never' \| 'auto'` | `'auto'` | Control zoom button visibility |
| `overflow` | `'hidden' \| 'visible'` | `'hidden'` | CSS overflow behavior |
| `zoomControlScale` | `number` | `1` | Scale factor for zoom controls |
| `backgroundColor` | `string` | `'rgba(0,0,0,0.85)'` | Container background color |
| `limitPan` | `boolean` | `false` | Prevent panning past image edges |
| `minPanScale` | `number` | `1.0001` | Minimum scale at which panning is enabled |
| `listeners` | `'auto' \| 'mouse and touch'` | `'mouse and touch'` | Event listener mode |
| `wheel` | `boolean` | `true` | Enable mouse wheel zoom |
| `wheelZoomFactor` | `number` | `0.2` | Zoom factor for mouse wheel |
| `autoHeight` | `boolean` | `false` | Calculate height from image dimensions |
| `draggableImage` | `boolean` | `false` | Make image draggable |
| `draggableOnPinch` | `boolean` | `false` | Allow dragging while pinching |
| `enableBrightnessControl` | `boolean` | `false` | Enable brightness adjustment controls |
| `brightnessStep` | `number` | `0.1` | Brightness adjustment increment |
| `minBrightness` | `number` | `0.1` | Minimum brightness value |
| `maxBrightness` | `number` | `2.0` | Maximum brightness value |
| `enableClickToZoom` | `boolean` | `false` | Enable click-to-zoom functionality |
| `clickToZoomScale` | `number` | `2.5` | Target scale when clicking to zoom |
## Outputs
| Output | Type | Description |
| ------------------- | -------------------------- | ---------------------------------------- |
| `zoomChanged` | `OutputEmitterRef` | Emits current scale when zoom changes |
| `brightnessChanged` | `OutputEmitterRef` | Emits current brightness when it changes |
## Methods
Access these methods via template reference or `viewChild`:
| Method | Parameters | Returns | Description |
| -------------------- | ------------------- | -------- | --------------------------------------------------- |
| `toggleZoom()` | - | `void` | Toggle between zoomed in/out |
| `zoomIn(value)` | `value: number` | `number` | Zoom in by value, returns new scale |
| `zoomOut(value)` | `value: number` | `number` | Zoom out by value, returns new scale |
| `brightnessIn()` | - | `number` | Increase brightness by step, returns new brightness |
| `brightnessOut()` | - | `number` | Decrease brightness by step, returns new brightness |
| `resetBrightness()` | - | `void` | Reset brightness to default (1.0) |
| `zoomToPoint(event)` | `event: MouseEvent` | `void` | Zoom to the clicked point (used internally) |
| `destroy()` | - | `void` | Clean up event listeners |
## Computed Properties
The component exposes several computed signals:
| Property | Type | Description |
| ----------------------- | --------- | ------------------------------------------- |
| `scale()` | `number` | Current zoom scale |
| `isZoomedIn()` | `boolean` | Whether image is zoomed in |
| `isDisabled()` | `boolean` | Whether zoom is disabled |
| `isDragging()` | `boolean` | Whether user is currently dragging |
| `isZoomLimitReached()` | `boolean` | Whether max zoom is reached |
| `maxScale()` | `number` | Maximum allowed scale |
| `isControl()` | `boolean` | Whether zoom controls should be shown |
| `brightness()` | `number` | Current brightness value |
| `isBrightnessControl()` | `boolean` | Whether brightness controls should be shown |
| `isBrightnessAtMin()` | `boolean` | Whether brightness is at minimum |
| `isBrightnessAtMax()` | `boolean` | Whether brightness is at maximum |
## Angular 20 Signals
This library fully embraces Angular 20's signals API:
### Input Signals
All component inputs are now signal-based for better performance and reactivity.
```typescript
// Before (Angular <16)
@Input() disabled: boolean = false;
// Now (Angular 20+)
disabled = input(false);
```
### Output Signals
Outputs use the new output() API:
```typescript
// Before
@Output() zoomChanged = new EventEmitter();
// Now
zoomChanged = output();
```
### Computed Signals
Derived state uses computed signals:
```typescript
isZoomedIn = computed(() => {
return this.scale() > 1;
});
```
## Browser Support
- Chrome/Edge (latest 2 versions)
- Firefox (latest 2 versions)
- Safari (latest 2 versions)
- iOS Safari (latest 2 versions)
- Chrome for Android (latest 2 versions)
## Requirements
- Angular 20.0.0 or higher
- TypeScript 5.8.0 or higher
- Node.js 18.19.1, 20.11.1, or 22.0.0+
## Migration from Older Versions
If you're upgrading from a pre-signals version:
1. **Inputs**: No changes needed in templates, binding syntax remains the same
2. **Outputs**: Event binding syntax remains the same
3. **ViewChild**: Update to `viewChild` signal (optional but recommended)
4. **Component properties**: Access computed properties by calling them: `component.scale()`
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
## Architecture
This library follows Angular best practices with a clean, professional architecture:
### Directory Structure
```
lib/
βββ models/ # Data models and interfaces
β βββ interfaces.model.ts
β βββ properties.model.ts
β βββ zoom-config.model.ts
β βββ transform-state.model.ts
β βββ brightness-state.model.ts
β βββ click-to-zoom.model.ts
β
βββ services/ # Business logic
β βββ brightness.service.ts # Angular service for brightness state
β βββ zoom-state.service.ts # Angular service for zoom state
β βββ ivy-pinch.service.ts # Core zoom/pan logic
β βββ touches.service.ts # Gesture detection
β
βββ components/
βββ containers/ # Smart components (with DI)
β βββ pinch-zoom/
β βββ pinch-zoom.container.ts
β βββ pinch-zoom.container.html
β βββ pinch-zoom.container.sass
β
βββ presentational/ # Dumb components (pure UI)
βββ zoom-controls/
βββ brightness-controls/
```
### Design Patterns
- **Smart/Dumb Component Pattern**: Clear separation between container components (business logic) and presentational components (pure UI)
- **Service-Based Architecture**: Business logic extracted into reusable Angular services
- **Signal-Based Reactivity**: All state management uses Angular 20 signals for optimal performance
- **Dependency Injection**: Proper use of Angular's DI system throughout
## License
MIT
## Credits
This library is built on the foundation of the original ngx-pinch-zoom, modernized for Angular 20 with extensive architectural improvements and new features.
**What's From the Original:**
- Core pinch-to-zoom mathematics and transform algorithms (IvyPinch)
- Touch and mouse gesture detection logic (Touches)
- Original zoom/pan calculations and constraints
**What's New in This Version:**
- Complete Angular 20 signals API migration (inputs, outputs, computed, effects)
- Professional architecture (models/, services/, containers/, presentational/)
- Smart/Dumb component pattern with explicit separation
- New features: brightness control, click-to-zoom
- Service-based state management (BrightnessService, ZoomStateService)
- 778 lines of comprehensive JSDoc documentation
- Modern TypeScript with full strict mode compliance
**Original Library:**
- **Author:** [Nikita Drozhzhin](https://github.com/drozhzhin-n-e) - Original creator and core zoom algorithms
- **Repository:** [drozhzhin-n-e/ngx-pinch-zoom](https://github.com/drozhzhin-n-e/ngx-pinch-zoom)
**Angular 19/20 Compatibility Fork:**
- **Contributors:**
- [Konstantin SchΓΌtte](https://www.meddv.de) (medDV-GmbH) - Angular 19/20 compatibility updates
- [BjΓΆrn Schmidt](https://www.meddv.de) (medDV-GmbH) - Angular 19/20 compatibility updates
- **Repository:** [medDV-GmbH/ngx-pinch-zoom](https://github.com/medDV-GmbH/ngx-pinch-zoom)
**Current Version:**
- **Maintainer:** [Brian Pooe](https://github.com/brianpooe) - Angular 20 signals migration, architecture redesign, new features (brightness control, click-to-zoom), comprehensive documentation
## Issues and Support
Please report issues on [GitHub Issues](https://github.com/brianpooe/ngx-pinch-zoom/issues)