{"id":22081640,"url":"https://github.com/sabine/svelte-crop-window","last_synced_at":"2025-07-24T14:32:53.684Z","repository":{"id":60980710,"uuid":"546872930","full_name":"sabine/svelte-crop-window","owner":"sabine","description":"Image and video cropper Svelte component with gesture and mouse support","archived":false,"fork":false,"pushed_at":"2024-01-11T02:29:43.000Z","size":37991,"stargazers_count":39,"open_issues_count":10,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-14T06:20:54.029Z","etag":null,"topics":["crop","gestures","image-cropper","rotate","svelte","svelte-component","video-cropper","zoom"],"latest_commit_sha":null,"homepage":"https://sabine.github.io/svelte-crop-window/","language":"Svelte","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sabine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-10-06T19:41:18.000Z","updated_at":"2025-05-15T15:26:10.000Z","dependencies_parsed_at":"2024-01-17T05:39:17.685Z","dependency_job_id":"3f9db776-df76-475a-8ca1-3b101713aa7b","html_url":"https://github.com/sabine/svelte-crop-window","commit_stats":{"total_commits":89,"total_committers":2,"mean_commits":44.5,"dds":0.2134831460674157,"last_synced_commit":"3950dd5814c106a34f169f53d9b9cc1d8dd89cf0"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/sabine/svelte-crop-window","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sabine%2Fsvelte-crop-window","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sabine%2Fsvelte-crop-window/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sabine%2Fsvelte-crop-window/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sabine%2Fsvelte-crop-window/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sabine","download_url":"https://codeload.github.com/sabine/svelte-crop-window/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sabine%2Fsvelte-crop-window/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266855920,"owners_count":23995583,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"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":["crop","gestures","image-cropper","rotate","svelte","svelte-component","video-cropper","zoom"],"created_at":"2024-11-30T23:24:53.079Z","updated_at":"2025-07-24T14:32:49.225Z","avatar_url":"https://github.com/sabine.png","language":"Svelte","funding_links":[],"categories":[],"sub_categories":[],"readme":"# svelte-crop-window\n\nA crop window component for images and videos that supports touch gestures (pinch zoom, rotate, pan), as well as mousewheel zoom, mouse-dragging the image, and rotating on right mouse button.\n\nCurrently looking for contributors / feature requests / feedback to help improve this component.\n\n![video cropper](/static/videocrop.gif)\n\nIf you can do code-review, that's very welcome.\n\nHere's a [demo page](https://sabine.github.io/svelte-crop-window/).\n\nAnd here's a [minimal REPL where you can play with the code](https://svelte.dev/repl/2db644efd08841958f2dd3209f00bf51?version=3.52.0) and a [fancier REPL](https://svelte.dev/repl/c246300e4ffd42a0b01ff318f7abd91d?version=3.52.0).\n\n## Installation\n\n```bash\nnpm install svelte-crop-window\n```\n\n## Basic use\n\nYou must wrap the `CropWindow` component with an Element that determines the height.\n```html\n\u003cscript\u003e\n    import { CropWindow, defaultValue } from 'svelte-crop-window';\n\n    let media = {\n        content_type: 'image',\n        url: '/svelte-crop-window/hintersee-3601004.jpg'\n    };\n\n    let value = { ...defaultValue };\n\u003c/script\u003e\n\n\u003cdiv style=\"height:20em\"\u003e\n    \u003cCropWindow bind:value {media} /\u003e\n\u003c/div\u003e\n```\n\n## `CropWindow.svelte` Component\n\n### Props\n\n| name      | type                                                                    | required | purpose                                                                                      |\n| --------- | ----------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------- |\n| `media`   | `Media`                     | ✓        | image or video to be cropped                                                                 |\n| `value`   | `CropValue` |          | value that describes [how to crop](#how-to-crop) - will be initialized if undefined |\n| `options` | [`Options`](#options)                                                               |          | options for the crop window and overlay, see below                                           |\n\n```typescript\ntype Media = {\n    content_type: 'image' | 'video';\n    url: string;\n}\n\ntype CropValue = {\n    position: { x: number; y: number };\n    aspect: number;\n    rotation: number;\n    scale: number; }\n}\n\nconst defaultValue: CropValue = {\n    position: { x: 0, y: 0 },\n    aspect: 1.0,\n    rotation: 0,\n    scale: 0\n};\n```\n\n### Options\n\n| name                 | type                | purpose                                                                                                                                                                                                                                                            |\n| -------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `shape`              | `'rect' \\| 'round'` | shape of the crop area (yes, an ellipse will work)                                                                                                                                                                                                                                             |\n| `crop_window_margin` | `number`            | Margin of the crop window, in pixels. The crop window will always scale to the maximum possible size in its containing element.                                                                                                                                    |\n| `overlay`            | a Svelte component  | The overlay component which visually highlights the crop area. You can pass your own Svelte component with props `options: T, gesture_in_progress: boolean, shape: 'rect' \\| 'round'` here, or use the included [Overlay.svelte](/src/lib/overlay/Overlay.svelte). |\n| `overlay_options`    | `T`                 | Options for your overlay component. See below for the options of the included overlay component.                                                                                                                                                                   |\n\n```typescript\nconst defaultOptions: Options\u003cOverlayOptions\u003e = {\n    shape: 'rect',\n    crop_window_margin: 10,\n    overlay: Overlay,\n    overlay_options: defaultOverlayOptions\n};\n```\n\n## `Overlay.svelte` Component\n\n### Options\n\n| name               | type      | purpose                                                              |\n| ------------------ | --------- | -------------------------------------------------------------------- |\n| `overlay_color`    | `string`  | the color of the overlay that covers everything except the crop area |\n| `line_color`       | `string`  | the color of the lines                                               |\n| `show_third_lines` | `boolean` | whether to show third lines or not when a gesture is in progress     |\n\n```typescript\nconst defaultOverlayOptions: OverlayOptions = {\n    overlay_color: 'rgb(11, 11, 11, 0.7)',\n    line_color: 'rgba(167, 167, 167, 0.5)',\n    show_third_lines: true\n};\n```\n\n## How to Crop\n\n### Display in HTML Without Actually Cropping:\n\n```html\n\u003cdiv\n    style=\"\n        position:relative; overflow:hidden;\n        height:{HEIGHT}px; width:{value.aspect * HEIGHT}px;\n        border-radius: {options.shape == 'round' ? '50%' : '0'}\"\n\u003e\n    \u003cvideo\n        style=\"\n    transform: translateX(-50%) translateY(-50%) rotate({value.rotation}deg);\n    height: {value.scale * HEIGHT}px;\n    margin-left: {HEIGHT * value.aspect / 2 + value.position.x * HEIGHT}px;\n    margin-top: {HEIGHT / 2 + value.position.y * HEIGHT}px;\n    max-width:none\"\n        src=\"{url}\"\n    /\u003e\n\u003c/div\u003e\n```\n\nNote: You must choose a `HEIGHT`, because the crop value is normalized against the target height.\n\n### Pseudo Code to Crop\n\n1. Choose a `target_height` and calculate the `target_width` for the cropped image:\n\n```javascript\nlet target_width = value.aspect * target_height;\n```\n\n2. Calculate factor `s` by which to scale:\n\n```javascript\nlet s = (value.scale * target_height) / media.height;\n```\n\n3. Scale media by `s`:\n\n```javascript\nlet resized_media = scale(media, s);\n```\n\n4. Rotate media by `value.rotation`:\n\n```javascript\nlet resized_and_rotated_media = rotate(resized_media, value.rotation);\n```\n\n5. Calculate top left position of the area to extract:\n\n```javascript\nlet left = (resized_and_rotated_media.width - target_width) / 2.0 \n            - value.x * target_height;\nlet top = (resized_and_rotated_media.height - target_height) / 2.0\n           - value.y * target_height;\n```\n\n6. Extract area:\n\n```javascript\nlet cropped_media =\n    extract_area(resized_and_rotated_media,\n                 left, top, target_width, target_height);\n```\n\n## What this component doesn't do\n\n1. Does not modify/crop the image, you have to do that by whatever means make sense for your application. Doesn't (yet) provide usable controls. Currently, you need to implement your own.\n2. Similar to the overlay, it would be nice to include some controls to make this more usable out of the box. Contributions are very welcome.\n\n## Developing\n\nOnce you've cloned the project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:\n\n```bash\nnpm run dev\n\n# or start the server and open the app in a new browser tab\nnpm run dev -- --open\n```\n\n## Acknowledgements\n\nOne big inspiration for this component was the Android library\n[uCrop by Yalantis](https://github.com/Yalantis/uCrop). What is particularly\nvaluable is that the developers shared their thought process in\n[this blog post](https://yalantis.com/blog/how-we-created-ucrop-our-own-image-cropping-library-for-android/).\n\nAnother very helpful resource was [svelte-easy-crop](https://www.npmjs.com/package/svelte-easy-crop)\nwhich gave me a basic understanding of how to implement a crop window component in Svelte\n(and HTML/JS in general).\n\nThere's no code reuse between either of these components and this one. All\ncalculations had to be recreated from textbook math.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsabine%2Fsvelte-crop-window","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsabine%2Fsvelte-crop-window","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsabine%2Fsvelte-crop-window/lists"}