vue-croppa
vue-croppa v2
This directory contains the vue-croppa package for Vue 3. Vue 2 consumers can stay on the 1.x release line.
Product direction
v2 is intentionally focused on upload-oriented image cropping: load an image, position/zoom it inside a crop viewport, and export or persist the crop. It is not intended to become a general-purpose image editor.
Interaction contract
The v2 rewrite preserves vue-croppa's defining WYSIWYG interaction model:
- The crop viewport is fixed and represents the exact output.
- The user moves and zooms the image underneath the viewport.
- There is no draggable or resizable crop-selection rectangle over the source image.
- Crop size/aspect is controlled by component configuration/layout, not by corner handles.
- Rotation and flips transform the image under the same fixed viewport.
- Exported pixels must match what is visible inside the viewport.
CropState.cropis an internal source-relative representation of what the fixed viewport currently sees; it is not the UI interaction model.
This is a product-level invariant for v2, not a compatibility detail.
Compatibility rule
v2 is a rewrite, not a feature reset. Existing v1 user capabilities are retained by default; any removal requires an explicit, separately reviewed decision.
The full inventory and migration requirements live in COMPATIBILITY.md.
In particular, v1 allows whitespace by default. Therefore CropState must be able to represent source-relative rectangles outside the 0..1 source bounds. preventWhiteSpace is an interaction constraint, not a fundamental limitation of the state model.
Foundation principles
- Vue 3 + TypeScript.
- Pure geometry/state logic is framework-independent and testable without a browser.
- Crop state is serializable and independent of the rendered viewport size.
- Display size, canvas backing resolution, and export dimensions are separate concepts.
- Pointer Events,
ResizeObserver, and modern browser APIs replace the legacy mouse/touch/polyfill stack. - The original source image stays immutable; rotation and flips are transformations, not re-encoding steps.
- v1 compatibility is handled through migration guidance/adapters rather than preserving v1 internals.
State model
The foundation uses a normalized crop rectangle plus image transforms:
interface CropState {
crop: {
x: number
y: number
width: number
height: number
}
rotation: 0 | 90 | 180 | 270
flipX: boolean
flipY: boolean
}
crop is expressed against the currently oriented source image in normalized source units. The source itself occupies 0..1, but the crop may use negative coordinates or dimensions greater than 1 when whitespace is visible. This preserves v1 behavior while keeping state stable across responsive layout changes and suitable for persistence or server-side reproduction.
Component
The Vue 3 Croppa component mounts a real image loader, fixed canvas viewport, Pointer Events interactions, remove control, and Blob/data URL export on the foundation core. File drop follows v1's default rule: it fills an empty viewport, and replaces an existing image only when replaceDrop is true. The component also supports the v1 disabled and per-interaction flags, reverse wheel direction, and inputAttrs. The docs preview mounts this component. COMPATIBILITY.md records the migration contract.
Basic usage:
<script setup lang="ts">
import { ref } from 'vue'
import { Croppa } from 'vue-croppa'
const cropper = ref<InstanceType<typeof Croppa> | null>(null)
</script>
<template>
<Croppa ref="cropper" :width="320" :height="320" initial-size="cover" />
<button @click="cropper?.promisedBlob('image/png')">Export</button>
</template>
The component exposes chooseFile(), setFile(file), remove(), move() and directional helpers, zoom() / zoomIn() / zoomOut(), rotate(step), flipX(), flipY(), getCanvas(), getContext(), addClipPlugin(), generateDataUrl(), generateBlob(callback), and promisedBlob(). It emits init, draw, loading-start, and loading-end at the corresponding lifecycle points, plus load-error when decoding fails. showLoading, loadingSize, and loadingColor control the optional indicator.
getMetadata() returns versioned v2 state. applyMetadata(metadata) accepts both v2 metadata and v1 { startX, startY, scale, orientation } metadata. It can queue metadata before an image loads. v2 restoration requires the same source dimensions and viewport aspect ratio. See MIGRATION.md for the Vue 2 to Vue 3 API changes.
Import vue-croppa/style.css with the component. The default export supports app.use(VueCroppa, { componentName: 'croppa' }); Croppa is also a named export. v-model shares an in-memory image and crop state with a passive preview. autoSizing uses ResizeObserver, and videoEnabled accepts supported video files and lets the user toggle playback with a double click.
Commands
Requires Node.js 22.12 or newer.
npm install
npm run check