feat: Add click-to-zoom functionality for precise inspection

Implement click-to-zoom feature perfect for anomaly detection and defect inspection workflows:

**New Input Signals:**
- enableClickToZoom: Enable/disable click-to-zoom (default: false)
- clickToZoomScale: Target zoom scale when clicking (default: 2.5)

**Implementation:**
- Click any point on image to zoom to that exact location
- Click again to zoom out back to original view
- Cursor changes to zoom-in icon when feature is enabled
- Smooth animated transitions
- Respects zoom limits (min/max scale)
- Works alongside existing touch/pinch zoom

**Use Cases:**
- Anomaly detection: Click suspicious areas to examine closely
- Defect inspection: Quickly zoom to specific spots
- Detail review: Fast workflow for inspecting multiple points

**Technical Details:**
- Added IvyPinch.zoomToPoint(clientX, clientY, targetScale) method
- Calculates click position relative to element bounds
- Applies zoom transform centered on clicked point
- Component wrapper method handles event and configuration
- CSS cursor feedback for better UX

Updated documentation with usage examples and testing scenarios.
This commit is contained in:
Claude 2025-11-15 16:39:01 +00:00
parent c0546dbf46
commit 1b7ed5adff
No known key found for this signature in database
6 changed files with 127 additions and 11 deletions

View file

@ -12,6 +12,7 @@ An Angular library for pinch-to-zoom functionality on touch-enabled devices and
- 🔄 **Pinch to Zoom** - Natural gesture support
- 🖱️ **Mouse Wheel Zoom** - Desktop-friendly
- 👆 **Double Tap** - Quick zoom in/out
- 🎯 **Click to Zoom** - Click any point to zoom in precisely
- ☀️ **Brightness Control** - Adjust image brightness on the fly
- 🎨 **Highly Configurable** - Extensive options
- 📦 **Standalone Component** - No module imports needed
@ -131,6 +132,39 @@ export class ControlsComponent {
}
```
### Click to Zoom
Enable click-to-zoom for quick inspection of specific areas:
```typescript
import { Component } from '@angular/core';
import { PinchZoomComponent } from '@meddv/ngx-pinch-zoom';
@Component({
selector: 'app-click-zoom',
standalone: true,
imports: [PinchZoomComponent],
template: `
<pinch-zoom [enableClickToZoom]="true" [clickToZoomScale]="2.5">
<img src="image.jpg" />
</pinch-zoom>
`,
})
export class ClickZoomComponent {}
```
This is particularly useful for:
- **Anomaly detection** - Click on suspicious areas to zoom in
- **Defect inspection** - Quickly examine specific spots
- **Detail review** - Fast workflow for inspecting multiple points
When enabled:
- Click any point on the image to zoom to that exact location
- Click again to zoom out back to original view
- Cursor changes to zoom-in icon to indicate the feature is active
### Brightness Control
Enable brightness controls alongside zoom controls:
@ -227,6 +261,8 @@ export class BrightnessControlsComponent {
| `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
@ -239,15 +275,16 @@ export class BrightnessControlsComponent {
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) |
| `destroy()` | - | `void` | Clean up event listeners |
| 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

View file

@ -76,6 +76,7 @@ transform: translate3d(${moveX}px, ${moveY}px, 0) scale(${scale})
| **Brightness up** | `brightnessIn()` | `pinch-zoom.component.ts` | Increases brightness by step |
| **Brightness down** | `brightnessOut()` | `pinch-zoom.component.ts` | Decreases brightness by step |
| **Reset brightness** | `resetBrightness()` | `pinch-zoom.component.ts` | Resets brightness to 1.0 |
| **Click to zoom** | `zoomToPoint()` | `pinch-zoom.component.ts` | Zooms to clicked point |
## Common Patterns
@ -208,6 +209,10 @@ enableBrightnessControl = input<boolean>(false); // Enable brightness UI
brightnessStep = input<number>(0.1); // Brightness step size
minBrightness = input<number>(0.1); // Min brightness
maxBrightness = input<number>(2.0); // Max brightness
// Click-to-zoom controls
enableClickToZoom = input<boolean>(false); // Enable click-to-zoom
clickToZoomScale = input<number>(2.5); // Target scale on click
```
### Internal Signals (Component State)
@ -489,6 +494,9 @@ flowchart TD
- [ ] Brightness controls (if enabled)
- [ ] Increase brightness button
- [ ] Decrease brightness button
- [ ] Click-to-zoom (if enabled)
- [ ] Click to zoom in to point
- [ ] Click again to zoom out
#### Edge Cases
@ -514,6 +522,8 @@ flowchart TD
- [ ] Set `brightnessStep=0.2` → larger brightness steps
- [ ] Set `minBrightness=0.5` → stops at 0.5 minimum
- [ ] Set `maxBrightness=1.5` → stops at 1.5 maximum
- [ ] Set `enableClickToZoom=true` → enables click-to-zoom
- [ ] Set `clickToZoomScale=3.0` → zooms to 3x on click
#### Performance Tests

View file

@ -695,6 +695,43 @@ export class IvyPinch {
return this.scale;
}
public zoomToPoint(clientX: number, clientY: number, targetScale: number): void {
// Update element position
this.getElementPosition();
// Calculate the click point relative to the element
const xRelativeToElement = clientX - this.elementPosition.left;
const yRelativeToElement = clientY - this.elementPosition.top;
// Clamp target scale to limits
let newScale = targetScale;
if (newScale > this.maxScale) {
newScale = this.maxScale;
}
if (newScale < (this.properties.minScale || 0)) {
newScale = this.properties.minScale || 0;
}
// If already at target scale or zoomed in, reset zoom
if (this.scale >= targetScale || this.scale > 1) {
this.resetScale();
return;
}
// Calculate the new position to keep the clicked point centered
this.scale = newScale;
this.zoomChanged(this.scale);
// Calculate the zoom offset to keep clicked point under cursor
const scaleRatio = newScale / this.initialScale;
this.moveX = this.initialMoveX - xRelativeToElement * (scaleRatio - 1);
this.moveY = this.initialMoveY - yRelativeToElement * (scaleRatio - 1);
this.centeringImage();
this.updateInitialValues();
this.transformElement(this.properties.transitionDuration || 200);
}
private setZoom(properties: { scale: number; center?: number[] }): void {
this.scale = properties.scale;
this.zoomChanged(this.scale);

View file

@ -1,4 +1,9 @@
<div class="pinch-zoom-content" [class.pz-dragging]="isDragging()">
<div
class="pinch-zoom-content"
[class.pz-dragging]="isDragging()"
[class.pz-click-to-zoom]="enableClickToZoom()"
(click)="enableClickToZoom() ? zoomToPoint($event) : null"
>
<ng-content></ng-content>
</div>

View file

@ -6,7 +6,13 @@
.pinch-zoom-content
height: inherit
.pz-dragging
.pz-dragging
cursor: all-scroll
.pz-click-to-zoom
cursor: zoom-in
.pz-click-to-zoom.pz-dragging
cursor: all-scroll

View file

@ -60,6 +60,10 @@ export class PinchZoomComponent implements OnInit, OnDestroy {
minBrightness = input<number>(0.1);
maxBrightness = input<number>(2.0);
// Click-to-zoom inputs
enableClickToZoom = input<boolean>(false);
clickToZoomScale = input<number>(2.5);
// Output signals
zoomChanged = output<number>();
brightnessChanged = output<number>();
@ -275,6 +279,23 @@ export class PinchZoomComponent implements OnInit, OnDestroy {
this.brightnessChanged.emit(1.0);
}
zoomToPoint(event: MouseEvent): void {
if (!this.enableClickToZoom() || this.isDisabled()) {
return;
}
// Prevent default to avoid conflicts
event.preventDefault();
event.stopPropagation();
// Get click coordinates
const clientX = event.clientX;
const clientY = event.clientY;
// Call IvyPinch method with target scale
this.pinchZoom?.zoomToPoint(clientX, clientY, this.clickToZoomScale());
}
detectLimitZoom(): void {
this.pinchZoom?.detectLimitZoom();
}