import { Component } from '@angular/core';
import { WrImageCropper } from 'ngwr/image-cropper';
@Component({ imports: [WrImageCropper] })
export class MyComponent {}Image Cropper
Crop an image and get the result as a Blob.
Installation
Import the class, and @use the entry point in your global styles: without it the component renders unstyled.
Basic
Drag the box to move it and a handle to resize it; (cropped) emits a Blob after each drag.
<input type="file" accept="image/*" (change)="onFileChange($event)" />
<wr-image-cropper [src]="src()" (cropped)="onCropped($event)" />Square (1:1)
aspectRatio locks the shape of the box.
<wr-image-cropper [src]="avatar" [aspectRatio]="1" />Load errors
An unreadable source fires (loadError) and sets status() to 'failed'; the cropper shows no message, so show your own. A URL on another origin needs CORS headers, or it fails the same way.
<wr-image-cropper #cropper [src]="file()" (loadError)="lastError.set($event)" />
@if (cropper.status() === 'failed') {
<wr-alert type="danger" title="This image could not be opened" [message]="message()" />
}import type { WrImageLoadError } from 'ngwr/image-cropper';
protected readonly file = signal<File | null>(null);
protected readonly lastError = signal<WrImageLoadError | null>(null);
// The error has the file's name and type, not a reason.
protected readonly message = computed(() => {
const error = this.lastError();
return `${error?.name ?? 'The file'} is not an image this browser can read.`;
});Export size
maxOutputSize caps the longest side of the exported image; a smaller crop is not upscaled.
<wr-image-cropper [src]="file()" [aspectRatio]="1" [maxOutputSize]="512" (cropped)="upload($event)" />Keyboard
Focus the crop box to move and resize it from the keyboard.
| Key | Does |
|---|---|
| Move by 1px. | |
| Move by 10px. | |
| Resize from the right or bottom edge: Left and Right change the width, Up and Down the height. | |
| Resize by 10px. |
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 |
maxOutputSize | Longest side of the exported image in pixels. null exports at the source resolution. | number | null | null |
(cropped) | Emits a Blob after each drag end. | Blob | — |
(loadError) | Fires when the browser cannot decode src, with { url, name, type, size }. | WrImageLoadError | — |
Readable state | Signals and methods on the component instance. | members | — |
cropRect | Computed crop rect in source-image pixel coordinates. | Signal<WrCropRect> | — |
status | 'empty', 'loading', 'loaded' or 'failed'. Resets when src changes. | Signal<WrImageCropperStatus> | — |
refresh() | Re-measure and re-emit (cropped) after the cropper was resized. | () => Promise<void> | — |
toBlob() / toDataUrl() | Read the current crop programmatically. | method | — |
CSS variables
Set these on the component's selector or a wrapper, not on :root: the defaults are declared on ngwr/image-cropper's own element.
| Variable | Default | Declared on |
|---|---|---|
--wr-image-cropper-empty-border | var(--wr-border-base) | .wr-image-cropper__empty |
--wr-image-cropper-empty-padding | var(--wr-space-xl) | .wr-image-cropper__empty |
--wr-image-cropper-empty-radius | var(--wr-border-radius-base) | .wr-image-cropper__empty |
--wr-image-cropper-font-family | var(--wr-font-family-base) | .wr-image-cropper |
