You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/cropper
dev ebc742e73b
fix(eidos): component-coherence audit FASE 2 — focus-ring, fields, segments, langs, API nits
4 months ago
..
README.md feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer 4 months ago
cropper.css fix(eidos): component-coherence audit FASE 2 — focus-ring, fields, segments, langs, API nits 4 months ago
cropper.svelte fix(eidos): component-coherence audit FASE 2 — focus-ring, fields, segments, langs, API nits 4 months ago
index.ts feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer 4 months ago
types.ts feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer 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.

  • fixedSize pins the crop and renders no resize handles (move-only) — shape='round' + fixedSize is an avatar picker. Respects aspect (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/maxScale bound it. Backed by the reusable soma/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's overflow:hidden. It follows border-radius, so a round selection cuts a circular hole — one mechanism for both shapes.
  • mask-bg / grid-line are 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() via bind:this (not an auto-fired event) — the consumer decides when to extract, the same shape as react-image-crop's getCroppedImg.
  • handle-drag / handle-resize emit once at gesture start, not per-frame (no haptic buzz — the virtual-list doctrine). handle-zoom is 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 in cropImage().
  • 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> is draggable=false (the browser's native image drag would otherwise cancel the pan — the bug Ark's zag machine guards the same way). Background shows grab when zoomed; the selection keeps move.

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.

Powered by TurnKey Linux.