Custom Stencil

The stencil is the draggable, resizable overlay that marks the crop area. You can write your own stencil component by following the stencil contract.

Built-in stencils

Use CircleStencil for circular crop (automatically 1:1):

CircleStencil example

No image loaded
<script lang="ts">
  import { Cropper, CircleStencil } from '@we-are-singular/svelte-chop-chop';
  import '@we-are-singular/svelte-chop-chop/themes/default';
</script>

<Cropper src="/avatar.jpg" stencil={CircleStencil} />

Stencil contract

A stencil component receives these props from the cropper engine:

PropTypeDescription
rectRectCurrent crop rect in viewport pixels: { x, y, width, height } .
onmove(delta: Point) => voidCall with pixel delta { x, y } to move the stencil.
onresize(handle: HandlePosition, delta: Point) => voidCall with handle position and delta to resize from a handle.
gridbooleanWhether to show the rule-of-thirds grid.
gridOnlyActivebooleanOnly show the grid during active interaction.

HandlePosition

HandlePosition is 'n' | 's' | 'e' | 'w' | 'nw' | 'ne' | 'sw' | 'se' .

Using DragHandle

The built-in DragHandle component handles pointer events and emits resize deltas. Use it for the corner and edge handles of your stencil:

<script lang="ts">
  import { DragHandle } from '@we-are-singular/svelte-chop-chop';
  import type { StencilProps, Point, HandlePosition } from '@we-are-singular/svelte-chop-chop';
  import { createDragHandler } from '@we-are-singular/svelte-chop-chop';

  let { rect, onmove, onresize, onresizestart, onresizeend }: StencilProps & {
    onmove: (delta: Point) => void;
    onresize: (handle: HandlePosition, delta: Point) => void;
    onresizestart?: () => void;
    onresizeend?: () => void;
  } = $props();

  const drag = createDragHandler({ onMove: onmove });
</script>

<div
  class="my-stencil"
  style="left:{rect.x}px; top:{rect.y}px; width:{rect.width}px; height:{rect.height}px"
  role="region"
  aria-label="Crop area"
  onpointerdown={drag.onpointerdown}
  onpointermove={drag.onpointermove}
  onpointerup={drag.onpointerup}
>
  <DragHandle position="nw" {onresize} {onresizestart} {onresizeend} />
  <DragHandle position="ne" {onresize} {onresizestart} {onresizeend} />
  <DragHandle position="sw" {onresize} {onresizestart} {onresizeend} />
  <DragHandle position="se" {onresize} {onresizestart} {onresizeend} />
</div>

Passing the custom stencil

<script lang="ts">
  import { Cropper } from '@we-are-singular/svelte-chop-chop';
  import MyStencil from './MyStencil.svelte';
</script>

<Cropper src="/photo.jpg" stencil={MyStencil} />

CropOverlay

Use the built-in CropOverlay component to render the dark semi-transparent overlay with a transparent cutout over the crop rect:

import { CropOverlay } from '@we-are-singular/svelte-chop-chop';

<CropOverlay rect={rect} imageBounds={imageBounds} />

GridOverlay

GridOverlay draws rule-of-thirds lines, a full grid, or a golden ratio guide:

import { GridOverlay } from '@we-are-singular/svelte-chop-chop';

<GridOverlay
  {cropRect}
  type="rule-of-thirds"
  visible={grid && (!gridOnlyActive || isDragging)}
/>