{"id":51377218,"url":"https://github.com/HoplaGeiss/ngx-image-forge","last_synced_at":"2026-07-05T21:00:33.995Z","repository":{"id":367401704,"uuid":"1280622424","full_name":"HoplaGeiss/ngx-image-forge","owner":"HoplaGeiss","description":"An Angular image editing library — crop, rotate, flip, and export images with zero dependencies.","archived":false,"fork":false,"pushed_at":"2026-06-25T20:49:21.000Z","size":236,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-25T22:03:51.004Z","etag":null,"topics":["angular","angular-library","canvas","crop","image-crop","image-editor","image-processing","signals","typescript"],"latest_commit_sha":null,"homepage":"https://hoplageiss.github.io/ngx-image-forge/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HoplaGeiss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-25T19:16:23.000Z","updated_at":"2026-06-25T20:49:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/HoplaGeiss/ngx-image-forge","commit_stats":null,"previous_names":["hoplageiss/ngx-image-forge"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/HoplaGeiss/ngx-image-forge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HoplaGeiss%2Fngx-image-forge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HoplaGeiss%2Fngx-image-forge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HoplaGeiss%2Fngx-image-forge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HoplaGeiss%2Fngx-image-forge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HoplaGeiss","download_url":"https://codeload.github.com/HoplaGeiss/ngx-image-forge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HoplaGeiss%2Fngx-image-forge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35168795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular-library","canvas","crop","image-crop","image-editor","image-processing","signals","typescript"],"created_at":"2026-07-03T14:00:27.945Z","updated_at":"2026-07-05T21:00:33.990Z","avatar_url":"https://github.com/HoplaGeiss.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["Images"],"readme":"# ngx-image-forge\n\nA signals-native, dependency-free Angular image editing library using the Canvas API.\n\n[![CI](https://github.com/HoplaGeiss/ngx-image-forge/actions/workflows/ci.yml/badge.svg)](https://github.com/HoplaGeiss/ngx-image-forge/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/ngx-image-forge)](https://www.npmjs.com/package/ngx-image-forge)\n[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)\n\n**[Live demo →](https://hoplageiss.github.io/ngx-image-forge/)**\n\n---\n\n## Features\n\n- **Signals-first** — internal state is pure signals, OnPush throughout\n- **Zoneless-compatible** — works with `provideExperimentalZonelessChangeDetection()`\n- **Zero runtime dependencies** — pure Canvas API, no third-party image libraries\n- **Three usage patterns** — embedded component, trigger directive, or programmatic service\n- **Non-destructive transforms** — rotate, flip, and crop are composed on export, never on the original\n- **Aspect ratio constraints** — free, fixed numeric ratio (e.g. `16/9`), or `1` for square\n- **Circular crop** — circular clip mask applied at export time\n- **Output control** — JPEG, PNG, or WebP with configurable quality and max dimensions\n- **Touch support** — drag and resize handles work on mobile\n- **Accessible** — keyboard-navigable overlay, focus management on open/close\n\n---\n\n## Requirements\n\n- Angular **19+**\n\n---\n\n## Installation\n\n```bash\nnpm install ngx-image-forge\n# or\npnpm add ngx-image-forge\n```\n\n---\n\n## Usage\n\nThere are three ways to use ngx-image-forge. Pick the one that fits your workflow.\n\n---\n\n### 1. Embedded component\n\nDrop `\u003cngx-image-editor\u003e` directly into your template and control it via a `viewChild` reference. Best for dedicated editing pages.\n\n```ts\nimport { NgxImageEditorComponent, NgxCroppedImage } from 'ngx-image-forge';\n\n@Component({\n  imports: [NgxImageEditorComponent],\n  template: `\n    \u003cngx-image-editor [image]=\"file\" [aspectRatio]=\"1\" #editor style=\"height: 500px\" /\u003e\n    \u003cbutton (click)=\"crop(editor)\"\u003eCrop\u003c/button\u003e\n  `,\n})\nexport class MyComponent {\n  file: File | null = null;\n\n  crop(editor: NgxImageEditorComponent): void {\n    const result: NgxCroppedImage = editor.crop();\n    console.log(result.dataUrl, result.blob);\n  }\n}\n```\n\n---\n\n### 2. Trigger directive\n\nAttach `ngxImageEditorTrigger` to any clickable element. Clicking it opens a full-screen overlay; the result is emitted via `(forgeResult)`. Best for inline \"edit\" buttons next to images.\n\n```ts\nimport { NgxImageEditorTriggerDirective, NgxCroppedImage } from 'ngx-image-forge';\n\n@Component({\n  imports: [NgxImageEditorTriggerDirective],\n  template: `\n    \u003cbutton\n      ngxImageEditorTrigger\n      [triggerImage]=\"file\"\n      [triggerAspectRatio]=\"16 / 9\"\n      (forgeResult)=\"onResult($event)\"\n    \u003e\n      Edit image\n    \u003c/button\u003e\n  `,\n})\nexport class MyComponent {\n  file: File | null = null;\n\n  onResult(result: NgxCroppedImage | null): void {\n    if (result) console.log('Cropped!', result.dataUrl);\n  }\n}\n```\n\n---\n\n### 3. Programmatic service\n\nInject `NgxImageForgeService` and call `open()` anywhere. Returns an `Observable` that emits once with the result (or `null` on cancel). Best for flows where the editor is opened from a service or complex logic.\n\n```ts\nimport { NgxImageForgeService, NgxCroppedImage } from 'ngx-image-forge';\n\n@Component({ ... })\nexport class MyComponent {\n  private readonly forge = inject(NgxImageForgeService);\n\n  openEditor(file: File): void {\n    this.forge.open({ image: file, aspectRatio: 1, roundCrop: true }).subscribe(result =\u003e {\n      if (result) console.log('Cropped!', result.dataUrl);\n    });\n  }\n}\n```\n\n---\n\n## `NgxImageEditorComponent` inputs\n\n| Input | Type | Default | Description |\n|---|---|---|---|\n| `image` | `File \\| string \\| null` | `null` | Source image — a `File` object or a URL string |\n| `aspectRatio` | `number \\| 'free'` | `'free'` | Crop constraint — a ratio (e.g. `16/9`) or `'free'` |\n| `maxWidth` | `number` | `0` | Maximum output width in px; `0` = unconstrained |\n| `maxHeight` | `number` | `0` | Maximum output height in px; `0` = unconstrained |\n| `quality` | `number` | `0.92` | JPEG/WebP encoding quality, 0–1 |\n| `outputFormat` | `'jpeg' \\| 'png' \\| 'webp'` | `'jpeg'` | Output MIME type |\n| `roundCrop` | `boolean` | `false` | Apply circular clip mask to the crop output |\n| `minCropWidth` | `number` | `50` | Minimum crop rectangle width in canvas px |\n| `minCropHeight` | `number` | `50` | Minimum crop rectangle height in canvas px |\n| `handleSize` | `number` | `10` | Side length in px of each resize handle square |\n| `handleHitArea` | `number` | `16` | Hit-test radius in px around each handle (increase for touch) |\n| `overlayOpacity` | `number` | `0.55` | Opacity of the dark overlay outside the crop rectangle |\n| `cropBorderColor` | `string` | `'#ffffff'` | CSS colour of the crop rectangle border |\n| `cropBorderWidth` | `number` | `1.5` | Width in px of the crop rectangle border |\n| `handleColor` | `string` | `'#ffffff'` | CSS colour of the resize handles |\n\n## `NgxImageEditorComponent` outputs\n\n| Output | Payload | Description |\n|---|---|---|\n| `imageLoaded` | `{ naturalWidth: number; naturalHeight: number }` | Fires when the image has loaded and is ready for editing |\n| `loadError` | `string \\| Event` | Fires when the image fails to load |\n| `imageCropped` | `NgxCroppedImage` | Fires immediately after a successful `crop()` call |\n\n## `NgxImageEditorComponent` methods\n\n| Method | Description |\n|---|---|\n| `crop()` | Performs the crop synchronously and returns an `NgxCroppedImage`. Throws if no image is loaded. |\n| `reset()` | Restores rotation, flip, and crop rectangle to their initial state. |\n| `rotate(degrees)` | Rotates the image by the given angle. Use multiples of 90 for lossless steps. |\n| `setRotation(degrees)` | Sets the exact rotation angle in degrees. |\n| `flipHorizontal()` | Toggles horizontal flip. |\n| `flipVertical()` | Toggles vertical flip. |\n\n---\n\n## `[ngxImageEditorTrigger]` directive inputs\n\n| Input | Type | Default | Description |\n|---|---|---|---|\n| `triggerImage` | `File \\| string \\| null` | `null` | Source image |\n| `triggerAspectRatio` | `number \\| 'free'` | `'free'` | Aspect ratio constraint |\n| `triggerMaxWidth` | `number` | `0` | Maximum output width in px |\n| `triggerMaxHeight` | `number` | `0` | Maximum output height in px |\n| `triggerQuality` | `number` | `0.92` | JPEG/WebP encoding quality |\n| `triggerOutputFormat` | `'jpeg' \\| 'png' \\| 'webp'` | `'jpeg'` | Output MIME type |\n| `triggerRoundCrop` | `boolean` | `false` | Apply circular clip mask |\n\n**Output:** `(forgeResult)` — emits `NgxCroppedImage` on confirm, `null` on cancel.\n\n---\n\n## `NgxCroppedImage`\n\nThe object returned by `crop()` and emitted by all result outputs.\n\n| Field | Type | Description |\n|---|---|---|\n| `blob` | `Blob` | Cropped image as a Blob, ready for upload |\n| `dataUrl` | `string` | Base64 data URL, suitable for `\u003cimg src\u003e` |\n| `width` | `number` | Output width in px |\n| `height` | `number` | Output height in px |\n| `originalWidth` | `number` | Natural width of the source image in px |\n| `originalHeight` | `number` | Natural height of the source image in px |\n\n---\n\n## Contributing\n\nPull requests are welcome. For significant changes please open an issue first.\n\n```bash\ngit clone https://github.com/HoplaGeiss/ngx-image-forge.git\ncd ngx-image-forge\npnpm install\nng serve   # starts the demo app at localhost:4200\n```\n\n---\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHoplaGeiss%2Fngx-image-forge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHoplaGeiss%2Fngx-image-forge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHoplaGeiss%2Fngx-image-forge/lists"}