# Image Cropper

> DOM-based crop UI. Drag the box to move, drag any of the eight handles to resize. Optional `[aspectRatio]` locks the box shape. `(cropped)` fires with a `Blob` after each drag end — wire to upload or preview.

Source: https://ngwr.dev/reference/components/image-cropper  
Kind: Composite

## Installation

```angular-ts
import { WrImageCropper } from 'ngwr/image-cropper';

@Component({ imports: [WrImageCropper] })
export class MyComponent {
  protected readonly src = signal<File | null>(null);

  onCropped(blob: Blob) {
    // upload blob, preview it, etc.
  }
}
```

## Basic

Pick an image, then drag the crop box. Live preview reflects each drag end.

```angular-html
<input type="file" accept="image/*" (change)="onFileChange($event)" />

<wr-image-cropper [src]="src()" (cropped)="onCropped($event)" />
```

## Square (1:1)

Lock the crop to a fixed aspect ratio.

```angular-html
<wr-image-cropper [src]="avatar" [aspectRatio]="1" />
```

## Keyboard

The crop window is focusable and fully operable without a pointer — moving it and resizing it are the same two gestures the drag offers, and they go through the same code, so `aspectRatio`, `minWidth` / `minHeight` and the image bounds hold for a keystroke exactly as they do for a drag.

| Key | Does |
| --- | --- |
| Arrow keys | Move the window by one pixel. Nothing mirrors under `dir="rtl"` — the image is not flipped. |
| Shift + arrows | The same, by ten pixels — the coarse modifier `wr-slider`, `wr-knob` and `wr-splitter` all use. |
| Alt + arrows | Resize from the east or south edge instead of moving — Alt + ArrowLeft / Alt + ArrowRight change the width, Alt + ArrowUp / Alt + ArrowDown the height. |
| Shift + Alt + arrows | Resize by ten pixels. |

`Alt` carries the mode switch rather than `Shift` precisely because `Shift` already means "coarse" everywhere else in the library, and the two have to compose. Two details worth knowing if you are wiring an upload to this: the live region announces `x, y, width × height` after each keystroke, and `(cropped)` fires once per GESTURE, on `keyup` — not on every repeat of a held arrow, which would render and encode a `Blob` many times a second.

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `src` | Image source. | `string \| File \| Blob \| null` | `null` |
| `aspectRatio` | Lock width / height. `null` = free. | `number \| null` | `null` |
| `minWidth` | Min crop width in display px. | `number` | `32` |
| `minHeight` | Min crop height in display px. | `number` | `32` |
| `outputType` | MIME type for `(cropped)`. | `'image/png' \| 'image/jpeg' \| 'image/webp'` | `'image/png'` |
| `outputQuality` | JPEG / WebP quality in [0, 1]. | `number` | `0.92` |
| `(cropped)` | Emits a Blob after each drag end. | `Blob` | `—` |
| `Readable state` | Signals and methods on the component instance. | `members` | — |
| `cropRect` | Computed crop rect in source-image pixel coordinates. | `Signal<WrCropRect>` | `—` |
| `toBlob() / toDataUrl()` | Read the current crop programmatically. | `method` | `—` |
