docs(cropper): add bilingual usage guides
This commit is contained in:
@@ -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 })
|
||||
|
||||
<Cropper
|
||||
src={src}
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
onCropComplete={(area) => 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.
|
||||
@@ -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<CropArea>()
|
||||
|
||||
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 (
|
||||
<div className="space-y-4">
|
||||
<Cropper
|
||||
src={src}
|
||||
shape="circle"
|
||||
className="size-72"
|
||||
onCropAreaChange={setArea}
|
||||
/>
|
||||
<button type="button" disabled={!area} onClick={saveAvatar}>
|
||||
Save avatar
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
@@ -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<CropArea>()
|
||||
|
||||
return (
|
||||
<Cropper
|
||||
src={src}
|
||||
alt="Product"
|
||||
aspectRatio={4 / 3}
|
||||
className="w-full max-w-xl rounded-xl"
|
||||
onCropAreaChange={setArea}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
@@ -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 })
|
||||
|
||||
<Cropper
|
||||
src={src}
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
onCropComplete={(area) => 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。
|
||||
@@ -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<CropArea>()
|
||||
|
||||
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 (
|
||||
<div className="space-y-4">
|
||||
<Cropper
|
||||
src={src}
|
||||
shape="circle"
|
||||
className="size-72"
|
||||
onCropAreaChange={setArea}
|
||||
/>
|
||||
<button type="button" disabled={!area} onClick={saveAvatar}>
|
||||
保存头像
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
@@ -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<CropArea>()
|
||||
|
||||
return (
|
||||
<Cropper
|
||||
src={src}
|
||||
alt="商品图片"
|
||||
aspectRatio={4 / 3}
|
||||
className="w-full max-w-xl rounded-xl"
|
||||
onCropAreaChange={setArea}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
用户可以拖拽图片、滚动缩放,或者在 Cropper 聚焦时使用方向键、`+`、`-`、`0` 和 `Home`。
|
||||
|
||||
## 外部控件 {#controls}
|
||||
|
||||
通过 `value` 和 `onValueChange` 控制裁剪状态,即可接入 Slider 或重置按钮。`minZoom` 最小为 `1`,保证图片始终覆盖整个视口。
|
||||
@@ -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()` 在浏览器内导出。
|
||||
Reference in New Issue
Block a user