diff --git a/packages/cropper/docs/en-US/advanced/geometry-and-export.mdx b/packages/cropper/docs/en-US/advanced/geometry-and-export.mdx new file mode 100644 index 0000000..003f30d --- /dev/null +++ b/packages/cropper/docs/en-US/advanced/geometry-and-export.mdx @@ -0,0 +1,49 @@ +--- +title: Geometry and export +description: Work with source coordinates, pure functions, and Canvas output. +order: 20 +toc: + - id: controlled-state + title: Controlled state + - id: geometry + title: Geometry functions + - id: export + title: Exporting an image + - id: cross-origin + title: Cross-origin images +--- + +## Controlled state {#controlled-state} + +```tsx +const [value, setValue] = React.useState({ x: 0, y: 0, zoom: 1 }) + + saveDraft(area)} +/> +``` + +`onCropAreaChange` runs whenever a valid crop changes. `onCropComplete` runs after drag or wheel interaction and after discrete keyboard operations, making it a better persistence boundary. + +## Geometry functions {#geometry} + +`getCoverScale`, `getCropperBounds`, `constrainCropperValue`, `getCropArea`, and `zoomCropperValueAtPoint` are DOM-independent pure functions that can be reused in custom interfaces and tests. + +## Exporting an image {#export} + +```tsx +const blob = await cropImage(src, area, { + type: "image/webp", + quality: 0.9, + width: 1200, +}) +``` + +Supplying only `width` or `height` preserves the crop aspect ratio. `shape: "circle"` clips through an ellipse; provide `background` when an opaque JPEG background is required. + +## Cross-origin images {#cross-origin} + +A remote image server must return CORS headers that permit Canvas use. Pass `crossOrigin="anonymous"` to `Cropper` and the matching `crossOrigin` option to `cropImage`; otherwise the browser may taint the Canvas and prevent Blob encoding. diff --git a/packages/cropper/docs/en-US/examples/avatar-crop.mdx b/packages/cropper/docs/en-US/examples/avatar-crop.mdx new file mode 100644 index 0000000..7586cc5 --- /dev/null +++ b/packages/cropper/docs/en-US/examples/avatar-crop.mdx @@ -0,0 +1,43 @@ +--- +title: Avatar crop +description: Build a circular avatar preview and export a fixed-size PNG. +order: 30 +toc: + - id: example + title: Example +--- + +## Example {#example} + +```tsx +import { Cropper, cropImage, type CropArea } from "@workspace/cropper" + +function AvatarCropper({ src }: { src: string }) { + const [area, setArea] = React.useState() + + async function saveAvatar() { + if (!area) return + const blob = await cropImage(src, area, { + height: 512, + shape: "circle", + type: "image/png", + width: 512, + }) + await uploadAvatar(blob) + } + + return ( +
+ + +
+ ) +} +``` diff --git a/packages/cropper/docs/en-US/guide/basic-usage.mdx b/packages/cropper/docs/en-US/guide/basic-usage.mdx new file mode 100644 index 0000000..3d96543 --- /dev/null +++ b/packages/cropper/docs/en-US/guide/basic-usage.mdx @@ -0,0 +1,48 @@ +--- +title: Basic usage +description: Add the stylesheet source and create an interactive image cropper. +order: 10 +toc: + - id: stylesheet + title: Stylesheet + - id: cropper + title: Cropper + - id: controls + title: External controls +--- + +## Stylesheet {#stylesheet} + +Add the package source to the application's Tailwind stylesheet: + +```css +@import "@workspace/cropper/globals.css"; +``` + +## Cropper {#cropper} + +Give the component an explicit width; `aspectRatio` determines its height: + +```tsx +import { Cropper, type CropArea } from "@workspace/cropper" + +function ProductImageCropper({ src }: { src: string }) { + const [area, setArea] = React.useState() + + return ( + + ) +} +``` + +Users can drag the image, scroll to zoom, or focus the Cropper and use the arrow, `+`, `-`, `0`, and `Home` keys. + +## External controls {#controls} + +Control the crop with `value` and `onValueChange` to connect a Slider or reset button. `minZoom` is clamped to at least `1`, ensuring that the image always covers the viewport. diff --git a/packages/cropper/docs/en-US/overview.mdx b/packages/cropper/docs/en-US/overview.mdx new file mode 100644 index 0000000..ff698ff --- /dev/null +++ b/packages/cropper/docs/en-US/overview.mdx @@ -0,0 +1,30 @@ +--- +title: Overview +description: Pan, zoom, calculate, and export image crops in React. +order: 1 +toc: + - id: package-role + title: Package role + - id: capabilities + title: Capabilities + - id: data-model + title: Data model +--- + +## Package role {#package-role} + +`@workspace/cropper` provides fixed-viewport image cropping. Users move and scale an image inside the viewport while the component reports the crop in source-image pixels. It owns interaction and geometry, but does not upload, store, or mutate the original file. + +## Capabilities {#capabilities} + +- Mouse and touch dragging. +- Pinch, wheel, double-click, and keyboard zooming. +- Controlled and uncontrolled state. +- Rectangular and circular viewports. +- Resize-aware image constraints. +- Standalone pure geometry functions. +- Canvas export to PNG, JPEG, or WebP Blob. + +## Data model {#data-model} + +In `CropperValue`, `x` and `y` are CSS-pixel offsets from the viewport center and `zoom` is relative to the cover fit. `CropArea` uses source-image pixels, so it can be sent to an image service or passed directly to `cropImage()` for browser-side export. diff --git a/packages/cropper/docs/zh-Hans/advanced/geometry-and-export.mdx b/packages/cropper/docs/zh-Hans/advanced/geometry-and-export.mdx new file mode 100644 index 0000000..e3a9794 --- /dev/null +++ b/packages/cropper/docs/zh-Hans/advanced/geometry-and-export.mdx @@ -0,0 +1,49 @@ +--- +title: 几何计算与导出 +description: 使用原图坐标、纯函数和 Canvas 输出裁剪结果。 +order: 20 +toc: + - id: controlled-state + title: 受控状态 + - id: geometry + title: 几何函数 + - id: export + title: 导出图片 + - id: cross-origin + title: 跨域图片 +--- + +## 受控状态 {#controlled-state} + +```tsx +const [value, setValue] = React.useState({ x: 0, y: 0, zoom: 1 }) + + saveDraft(area)} +/> +``` + +`onCropAreaChange` 在有效裁剪区域变化时调用;`onCropComplete` 在拖拽或滚轮交互结束以及离散键盘操作后调用,更适合触发持久化。 + +## 几何函数 {#geometry} + +`getCoverScale`、`getCropperBounds`、`constrainCropperValue`、`getCropArea` 和 `zoomCropperValueAtPoint` 都是无 DOM 依赖的纯函数,可在自定义 UI 或测试中单独使用。 + +## 导出图片 {#export} + +```tsx +const blob = await cropImage(src, area, { + type: "image/webp", + quality: 0.9, + width: 1200, +}) +``` + +只指定 `width` 或 `height` 时会保持裁剪区域宽高比。`shape: "circle"` 会使用椭圆路径裁剪;需要 JPEG 背景色时可以提供 `background`。 + +## 跨域图片 {#cross-origin} + +跨域 URL 必须由图片服务器返回允许 Canvas 使用的 CORS 响应头,并同时向 `Cropper` 和 `cropImage` 传入 `crossOrigin="anonymous"`。否则浏览器会将 Canvas 标记为 tainted,无法编码 Blob。 diff --git a/packages/cropper/docs/zh-Hans/examples/avatar-crop.mdx b/packages/cropper/docs/zh-Hans/examples/avatar-crop.mdx new file mode 100644 index 0000000..1e72563 --- /dev/null +++ b/packages/cropper/docs/zh-Hans/examples/avatar-crop.mdx @@ -0,0 +1,43 @@ +--- +title: 头像裁剪 +description: 创建圆形头像预览并导出固定尺寸的 PNG。 +order: 30 +toc: + - id: example + title: 示例 +--- + +## 示例 {#example} + +```tsx +import { Cropper, cropImage, type CropArea } from "@workspace/cropper" + +function AvatarCropper({ src }: { src: string }) { + const [area, setArea] = React.useState() + + async function saveAvatar() { + if (!area) return + const blob = await cropImage(src, area, { + height: 512, + shape: "circle", + type: "image/png", + width: 512, + }) + await uploadAvatar(blob) + } + + return ( +
+ + +
+ ) +} +``` diff --git a/packages/cropper/docs/zh-Hans/guide/basic-usage.mdx b/packages/cropper/docs/zh-Hans/guide/basic-usage.mdx new file mode 100644 index 0000000..7d461ef --- /dev/null +++ b/packages/cropper/docs/zh-Hans/guide/basic-usage.mdx @@ -0,0 +1,48 @@ +--- +title: 基础使用 +description: 添加样式入口并创建一个可交互的图片裁剪器。 +order: 10 +toc: + - id: stylesheet + title: 样式入口 + - id: cropper + title: 裁剪器 + - id: controls + title: 外部控件 +--- + +## 样式入口 {#stylesheet} + +在应用的 Tailwind 样式入口中添加: + +```css +@import "@workspace/cropper/globals.css"; +``` + +## 裁剪器 {#cropper} + +组件需要明确的宽度,`aspectRatio` 会确定高度: + +```tsx +import { Cropper, type CropArea } from "@workspace/cropper" + +function ProductImageCropper({ src }: { src: string }) { + const [area, setArea] = React.useState() + + return ( + + ) +} +``` + +用户可以拖拽图片、滚动缩放,或者在 Cropper 聚焦时使用方向键、`+`、`-`、`0` 和 `Home`。 + +## 外部控件 {#controls} + +通过 `value` 和 `onValueChange` 控制裁剪状态,即可接入 Slider 或重置按钮。`minZoom` 最小为 `1`,保证图片始终覆盖整个视口。 diff --git a/packages/cropper/docs/zh-Hans/overview.mdx b/packages/cropper/docs/zh-Hans/overview.mdx new file mode 100644 index 0000000..2dab544 --- /dev/null +++ b/packages/cropper/docs/zh-Hans/overview.mdx @@ -0,0 +1,30 @@ +--- +title: 概览 +description: 在 React 中完成图片平移、缩放、裁剪计算与 Canvas 导出。 +order: 1 +toc: + - id: package-role + title: Package 定位 + - id: capabilities + title: 功能 + - id: data-model + title: 数据模型 +--- + +## Package 定位 {#package-role} + +`@workspace/cropper` 提供固定视口式图片裁剪:用户在裁剪视口内移动和缩放图片,组件持续返回原图像素坐标中的裁剪区域。组件只负责交互和几何计算,不负责上传、存储或修改原始文件。 + +## 功能 {#capabilities} + +- 鼠标和触摸拖拽。 +- 双指、滚轮、双击和键盘缩放。 +- 受控与非受控状态。 +- 矩形和圆形视口。 +- 响应容器尺寸变化并自动约束图片位置。 +- 独立的纯几何函数。 +- 使用 Canvas 导出 PNG、JPEG 或 WebP Blob。 + +## 数据模型 {#data-model} + +`CropperValue` 中的 `x`、`y` 是相对视口中心的 CSS 像素偏移,`zoom` 是相对于 cover fit 的缩放倍数。`CropArea` 使用原图像素,因此可以直接交给服务端图片处理服务,也可以传给 `cropImage()` 在浏览器内导出。