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:
parent
c0546dbf46
commit
1b7ed5adff
6 changed files with 127 additions and 11 deletions
55
README.md
55
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue