Composite

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.

Installation

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.

<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.

<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.

Focus is on the crop window. Steps are in DISPLAY pixels, then scaled to the source image.
KeyDoes
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 + arrowsResize 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

NameDescriptionTypeDefault
srcImage source.string | File | Blob | nullnull
aspectRatioLock width / height. null = free.number | nullnull
minWidthMin crop width in display px.number32
minHeightMin crop height in display px.number32
outputTypeMIME type for (cropped).'image/png' | 'image/jpeg' | 'image/webp''image/png'
outputQualityJPEG / WebP quality in [0, 1].number0.92
(cropped)Emits a Blob after each drag end.Blob
Readable stateSignals and methods on the component instance.members
cropRectComputed crop rect in source-image pixel coordinates.Signal<WrCropRect>
toBlob() / toDataUrl()Read the current crop programmatically.method