|
|
4 months ago | |
|---|---|---|
| .. | ||
| README.md | 4 months ago | |
| cropper.css | 4 months ago | |
| cropper.svelte | 4 months ago | |
| index.ts | 4 months ago | |
| types.ts | 4 months ago | |
README.md
Cropper (eidos)
The styled, configured image cropper — a movable / resizable selection over the
image, with a darken-outside mask, rule-of-thirds grid and four corner handles. It
composes the framework <Image>; the soma owns the crop rect, the drag/resize math
and the <canvas> extraction.
<script>
import Cropper from '$uix/eidos/components/cropper';
let cr; let crop = $state({ x: 0.15, y: 0.15, width: 0.7, height: 0.7 });
</script>
<Cropper bind:this={cr} {src} bind:crop aspect={1} shape="round"
onCropComplete={(r) => save(r.blob)} />
<button onclick={() => cr.cropImage()}>Crop</button>
Baseline
No Air baseline. Cropper is a new component (2026-06-10); no air/ predecessor.
Designed fresh against the references below.
Comparativa
| Capability | Ark image-cropper | react-image-crop | react-easy-crop | Cropper.js | → UIX |
|---|---|---|---|---|---|
| Selection over a fixed image | ✅ | ✅ | — (pan/zoom) | ✅ | ✅ |
| Corner handles + thirds grid | ✅ | ✅ | partial | ✅ | ✅ |
| Aspect lock | ✅ | ✅ | ✅ | ✅ | ✅ |
| Circular crop | — | ✅ | ✅ cropShape |
partial | ✅ |
| Zoom / pan of the image | — | — | ✅ | ✅ | ✅ (reusable layer) |
| Fixed-size crop (move-only) | — | ✅ (locked) | ✅ | — | ✅ fixedSize |
| Blob output (canvas) | headless | you extract | helper | ✅ | ✅ built-in |
| Sound / haptic | ❌ | ❌ | ❌ | ❌ | ✅ sema |
The Ark image-cropper (the reference the user pointed to — Root · Viewport · Image · Selection · Grid · Handle) is headless and emits the rect; UIX mirrors the
anatomy and adds the canvas Blob + the circular mask + the perceptual layer.
Props
ProviderProps (soma) — src / crop / aspect / shape / minSize /
maxSize / fixedSize / minScale / maxScale / disabled / onCropChange /
onCropComplete (see soma README), plus the cropImage() method exposed via
bind:this.
fixedSizepins the crop and renders no resize handles (move-only) —shape='round'+fixedSizeis an avatar picker. Respectsaspect(use 1:1 for a true circle).- Zoom — wheel / pinch zoom centered on the cursor, drag the background to
pan, plus −/slider/+/reset controls.
minScale/maxScalebound it. Backed by the reusablesoma/layers/zoom-pan(createZoomPan).
Decisiones
- Composition over reimplementation. Renders the framework
<Image>for the preview; never re-declares an image surface. - Normalized 0–1 crop rect. Resolution-independent. The viewport's aspect-ratio is set to the image's natural aspect (no letterbox) so the rect maps 1:1 to the image for the canvas extraction.
- Darken-mask is a box-shadow on the Selection (
0 0 0 9999px), clipped by the viewport'soverflow:hidden. It followsborder-radius, so aroundselection cuts a circular hole — one mechanism for both shapes. mask-bg/grid-lineare recipe tokens carrying literal rgba (overlay tints, not theme colors) — kept out of the component CSS to satisfy the raw-color contract, and themeable.cropImage()viabind:this(not an auto-fired event) — the consumer decides when to extract, the same shape asreact-image-crop'sgetCroppedImg.handle-drag/handle-resizeemit once at gesture start, not per-frame (no haptic buzz — the virtual-list doctrine).handle-zoomis throttled.- Zoom is a reusable layer, not cropper-local.
soma/layers/zoom-pan(createZoomPan) owns scale + offset + clamp + cursor-centered zoom math, so an image-viewer / diagram-pan / map can reuse it. The cropper wires wheel / pan / controls to it and inverts the transform incropImage(). - Two zones (Cropper.js model). Dragging the selection moves the crop area
(it captures the gesture). Dragging the background image pans it to set the
focus point (when zoomed). The 8 handles resize. The
<img>isdraggable=false(the browser's native image drag would otherwise cancel the pan — the bug Ark's zag machine guards the same way). Background showsgrabwhen zoomed; the selection keepsmove.
Recipe tokens
Public, themeable — --cropper-*: viewport-bg/radius, mask-bg, grid-line,
selection-border(-width), handle-size/bg/border(-width).
eidos-lint cropper → invalid 0 (13 morfo-backed selectors). CSS imported from the
wrapper (code-split) — intentionally NOT in eidos/index.css.
Gaps
| Gap | Disposition | Notes |
|---|---|---|
| Keyboard move/resize/zoom of the selection | diferir | v1 is pointer-driven; arrows-to-move (FloatPanel grab-mode style) is a follow-up. |
| Edge handles (n/e/s/w) | diferir | The morfo declares all 8 corners; eidos renders the 4 corners. Edges are a quick add. |
| Aspect exactly at a boundary | diferir | Edge clamping may relax the locked ratio at the very edge; drag inward restores it. |
| External-CORS images taint the canvas | diferir | Blob URLs (the ImagePicker flow) are fine; cross-origin <img> needs CORS headers to export. |
| Baking ImagePicker filter/rotation into the crop | diferir | v1 crops the source src; applying the upstream filter/rotation in the canvas is a pipeline follow-up. |