Browse Source

docs(@vben/docs): 添加 VCropper 图片裁剪组件文档

- 新增中文文档 docs/src/components/common-ui/vben-cropper.md
- 新增英文文档 docs/src/en/components/common-ui/vben-cropper.md
- 新增基础用法示例 demos/vben-cropper/basic
- 新增固定比例裁剪示例 demos/vben-cropper/aspect-ratio
- 更新侧边栏配置添加 Cropper 入口
pull/7904/head
yuan.ji 2 weeks ago
parent
commit
21ced19437
  1. 4
      docs/.vitepress/config/en.mts
  2. 4
      docs/.vitepress/config/zh.mts
  3. 172
      docs/src/components/common-ui/vben-cropper.md
  4. 86
      docs/src/demos/vben-cropper/aspect-ratio/index.vue
  5. 54
      docs/src/demos/vben-cropper/basic/index.vue
  6. 159
      docs/src/en/components/common-ui/vben-cropper.md

4
docs/.vitepress/config/en.mts

@ -198,6 +198,10 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
link: 'common-ui/vben-ellipsis-text',
text: 'EllipsisText',
},
{
link: 'common-ui/vben-cropper',
text: 'Cropper',
},
],
},
];

4
docs/.vitepress/config/zh.mts

@ -196,6 +196,10 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
link: 'common-ui/vben-ellipsis-text',
text: 'EllipsisText 省略文本',
},
{
link: 'common-ui/vben-cropper',
text: 'Cropper 图片裁剪',
},
],
},
];

172
docs/src/components/common-ui/vben-cropper.md

@ -0,0 +1,172 @@
---
outline: deep
---
# Vben Cropper 图片裁剪
`VCropper` 是一个纯原生实现的图片裁剪组件,支持自由比例和固定比例裁剪,可通过方法调用获取裁剪后的图片。
> 如果文档内没有参数说明,可以尝试在在线示例内寻找
::: info 写在前面
如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
:::
## 基础用法
最基本的图片裁剪,支持自由比例调整。
<DemoPreview dir="demos/vben-cropper/basic" />
## 固定比例裁剪
通过 `aspectRatio` 属性设置裁剪比例,格式为 `"宽:高"`,如 `"1:1"`、`"16:9"`、`"3:4"` 等。
<DemoPreview dir="demos/vben-cropper/aspect-ratio" />
## API
### Props
| 属性名 | 描述 | 类型 | 默认值 |
| ------------- | ------------------------------------- | -------- | ------ |
| `img` | 图片地址(必填) | `string` | - |
| `width` | 容器宽度 | `number` | `500` |
| `height` | 容器高度 | `number` | `400` |
| `aspectRatio` | 裁剪比例,格式如 `"1:1"`、`"16:9"` 等 | `string` | - |
### Methods
通过 `ref` 调用组件方法:
```vue
<script setup lang="ts">
import { ref } from 'vue';
import { VCropper } from '@vben/common-ui';
const cropperRef = ref<InstanceType<typeof VCropper>>();
const handleCrop = async () => {
const result = await cropperRef.value?.getCropImage();
// result 为 Blob 或 base64 字符串
};
</script>
```
#### getCropImage
裁剪并获取图片。
```ts
interface GetCropImageOptions {
/** 输出图片格式 */
format?: 'image/jpeg' | 'image/png';
/** 压缩质量(0-1),仅对 jpeg 格式有效 */
quality?: number;
/** 输出类型 */
outputType?: 'base64' | 'blob';
/** 目标宽度(可选,不传则为原始裁剪宽度) */
targetWidth?: number;
/** 目标高度(可选,不传则为原始裁剪高度) */
targetHeight?: number;
}
getCropImage(
format?: 'image/jpeg' | 'image/png',
quality?: number,
outputType?: 'base64' | 'blob',
targetWidth?: number,
targetHeight?: number,
): Promise<Blob | string | undefined>
```
**参数说明:**
| 参数 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| `format` | `'image/jpeg' \| 'image/png'` | `'image/png'` | 输出图片格式 |
| `quality` | `number` | `0.92` | 压缩质量(0-1),仅 jpeg 有效 |
| `outputType` | `'base64' \| 'blob'` | `'blob'` | 输出类型,base64 字符串或 Blob 对象 |
| `targetWidth` | `number` | - | 目标宽度,不传则使用原始裁剪宽度 |
| `targetHeight` | `number` | - | 目标高度,不传则使用原始裁剪高度 |
## 功能特性
### 裁剪操作
- **拖拽移动** - 拖拽裁剪框中心区域移动裁剪位置
- **边角调整** - 拖拽四角调整裁剪框大小
- **边缘调整** - 拖拽四边中点调整单边
### 比例控制
- **自由比例** - 不设置 `aspectRatio` 时,可自由调整任意比例
- **固定比例** - 设置 `aspectRatio` 后,裁剪框始终保持设定比例
### 高清屏适配
组件自动适配 Retina 等高清屏幕,保证输出图片清晰无模糊。
### 图片适配
- 图片自动等比缩放以完整显示在容器内
- 支持本地图片和网络图片
- 自动处理跨域图片
## 使用示例
```vue
<script setup lang="ts">
import { ref } from 'vue';
import { VCropper } from '@vben/common-ui';
const cropperRef = ref<InstanceType<typeof VCropper>>();
const imageUrl = ref('https://example.com/image.jpg');
const croppedImage = ref('');
// 获取裁剪后的 Blob 对象
const handleCropBlob = async () => {
const blob = await cropperRef.value?.getCropImage('image/jpeg', 0.9, 'blob');
if (blob instanceof Blob) {
// 上传到服务器或创建预览URL
const url = URL.createObjectURL(blob);
croppedImage.value = url;
}
};
// 获取裁剪后的 base64 字符串
const handleCropBase64 = async () => {
const base64 = await cropperRef.value?.getCropImage('image/png', 1, 'base64');
if (typeof base64 === 'string') {
croppedImage.value = base64;
}
};
// 导出指定尺寸
const handleCropWithSize = async () => {
const blob = await cropperRef.value?.getCropImage(
'image/jpeg',
0.9,
'blob',
200, // 目标宽度
200, // 目标高度
);
};
</script>
<template>
<div>
<VCropper
ref="cropperRef"
:img="imageUrl"
:width="500"
:height="400"
aspect-ratio="1:1"
/>
<button @click="handleCropBlob">裁剪</button>
<img v-if="croppedImage" :src="croppedImage" />
</div>
</template>
```

86
docs/src/demos/vben-cropper/aspect-ratio/index.vue

@ -0,0 +1,86 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { VCropper } from '@vben/common-ui';
const cropperRef = ref<InstanceType<typeof VCropper>>();
const aspectRatio = ref('1:1');
const imageUrl = ref('https://picsum.photos/seed/cropper-ratio/800/600');
const croppedImage = ref('');
const aspectOptions = [
{ label: '1:1 (正方形)', value: '1:1' },
{ label: '16:9 (宽屏)', value: '16:9' },
{ label: '4:3 (标准)', value: '4:3' },
{ label: '3:4 (竖版)', value: '3:4' },
{ label: '3:2 (照片)', value: '3:2' },
];
const handleCrop = async () => {
const blob = await cropperRef.value?.getCropImage('image/jpeg', 0.9, 'blob');
if (blob instanceof Blob) {
croppedImage.value = URL.createObjectURL(blob);
}
};
const handleReset = () => {
croppedImage.value = '';
imageUrl.value = `https://picsum.photos/seed/cropper-${Date.now()}/800/600`;
};
</script>
<template>
<div>
<div class="mb-4">
<label class="text-sm text-gray-500 mr-2">选择比例:</label>
<select v-model="aspectRatio" class="px-3 py-1 border rounded text-sm">
<option
v-for="option in aspectOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</option>
</select>
</div>
<VCropper
ref="cropperRef"
:img="imageUrl"
:width="500"
:height="300"
:aspect-ratio="aspectRatio"
/>
<div class="mt-4 flex gap-2">
<button
class="px-4 py-2 bg-blue-500 rounded hover:bg-blue-600"
@click="handleCrop"
>
裁剪图片
</button>
<button
class="px-4 py-2 bg-gray-500 rounded hover:bg-gray-600"
@click="handleReset"
>
重置
</button>
</div>
<div v-if="croppedImage" class="mt-4">
<p class="text-sm text-gray-500 mb-2">
裁剪结果 (比例: {{ aspectRatio }}):
</p>
<img :src="croppedImage" class="max-w-full rounded border" />
</div>
<div class="mt-4">
<p class="text-sm text-gray-500">提示:</p>
<ul class="mt-2 text-xs text-gray-400 list-disc pl-4">
<li>设置固定比例后裁剪框始终维持该比例</li>
<li>切换比例会自动重新计算裁剪框大小</li>
<li>比例格式为 "宽:高" "16:9"</li>
</ul>
</div>
</div>
</template>

54
docs/src/demos/vben-cropper/basic/index.vue

@ -0,0 +1,54 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { VCropper } from '@vben/common-ui';
const cropperRef = ref<InstanceType<typeof VCropper>>();
const imageUrl = ref('https://picsum.photos/seed/cropper-demo/800/600');
const croppedImage = ref('');
const handleCrop = async () => {
const blob = await cropperRef.value?.getCropImage('image/jpeg', 0.9, 'blob');
if (blob instanceof Blob) {
croppedImage.value = URL.createObjectURL(blob);
}
};
const handleReset = () => {
croppedImage.value = '';
//
imageUrl.value = `https://picsum.photos/seed/cropper-${Date.now()}/800/600`;
};
</script>
<template>
<div>
<VCropper ref="cropperRef" :img="imageUrl" :width="500" :height="300" />
<div class="mt-4 flex gap-2">
<button
class="px-4 py-2 bg-blue-500 rounded hover:bg-blue-600"
@click="handleCrop"
>
裁剪图片
</button>
<button
class="px-4 py-2 bg-gray-500 rounded hover:bg-gray-600"
@click="handleReset"
>
重置
</button>
</div>
<div v-if="croppedImage" class="mt-4">
<p class="text-sm text-gray-500 mb-2">裁剪结果:</p>
<img :src="croppedImage" class="max-w-full rounded border" />
</div>
<div class="mt-4">
<p class="text-sm text-gray-500">提示:</p>
<ul class="mt-2 text-xs text-gray-400 list-disc pl-4">
<li>拖拽裁剪框中心区域可移动裁剪位置</li>
<li>拖拽四角或四边可调整裁剪框大小</li>
<li>默认为自由比例可调整为任意比例</li>
</ul>
</div>
</div>
</template>

159
docs/src/en/components/common-ui/vben-cropper.md

@ -0,0 +1,159 @@
---
outline: deep
---
# Vben Cropper Image Cropping
`VCropper` is a pure native image cropping component that supports both free and fixed aspect ratio cropping, with method-based access to cropped results.
> If some details are not obvious from the docs, check the live demos as well.
::: info Note
If you feel the current component implementation doesn't meet your needs, you can use native components directly or create your own component. The components provided by the framework are not constraints - use them at your discretion.
:::
## Basic Usage
Basic image cropping with free aspect ratio adjustment.
<DemoPreview dir="demos/vben-cropper/basic" />
## Fixed Aspect Ratio
Set the cropping ratio via the `aspectRatio` prop. The format is `"width:height"`, e.g. `"1:1"`, `"16:9"`, `"3:4"`.
<DemoPreview dir="demos/vben-cropper/aspect-ratio" />
## API
### Props
| Property | Description | Type | Default |
| ------------- | ---------------------------------- | -------- | ------- |
| `img` | Image URL (required) | `string` | - |
| `width` | Container width | `number` | `500` |
| `height` | Container height | `number` | `400` |
| `aspectRatio` | Crop ratio, e.g. `"1:1"`, `"16:9"` | `string` | - |
### Methods
Call component methods via `ref`:
```vue
<script setup lang="ts">
import { ref } from 'vue';
import { VCropper } from '@vben/common-ui';
const cropperRef = ref<InstanceType<typeof VCropper>>();
const handleCrop = async () => {
const result = await cropperRef.value?.getCropImage();
// result is a Blob or base64 string
};
</script>
```
#### getCropImage
Crop and retrieve the image.
```ts
getCropImage(
format?: 'image/jpeg' | 'image/png',
quality?: number,
outputType?: 'base64' | 'blob',
targetWidth?: number,
targetHeight?: number,
): Promise<Blob | string | undefined>
```
**Parameters:**
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `format` | `'image/jpeg' \| 'image/png'` | `'image/png'` | Output image format |
| `quality` | `number` | `0.92` | Compression quality (0-1), only effective for jpeg |
| `outputType` | `'base64' \| 'blob'` | `'blob'` | Output type, base64 string or Blob object |
| `targetWidth` | `number` | - | Target width, defaults to original crop width if omitted |
| `targetHeight` | `number` | - | Target height, defaults to original crop height if omitted |
## Features
### Cropping Operations
- **Drag to Move** - Drag the center area of the crop box to move its position
- **Corner Resize** - Drag the four corners to resize the crop box
- **Edge Resize** - Drag the midpoints of edges to adjust a single side
### Aspect Ratio Control
- **Free Ratio** - Without `aspectRatio`, adjust the crop box to any ratio
- **Fixed Ratio** - With `aspectRatio` set, the crop box maintains the specified ratio
### HiDPI Support
The component automatically adapts to Retina and other high-DPI screens, ensuring crisp output images.
### Image Fitting
- Images are automatically scaled to fit within the container
- Supports both local and remote images
- Cross-origin images are handled automatically
## Usage Example
```vue
<script setup lang="ts">
import { ref } from 'vue';
import { VCropper } from '@vben/common-ui';
const cropperRef = ref<InstanceType<typeof VCropper>>();
const imageUrl = ref('https://example.com/image.jpg');
const croppedImage = ref('');
// Get cropped Blob
const handleCropBlob = async () => {
const blob = await cropperRef.value?.getCropImage('image/jpeg', 0.9, 'blob');
if (blob instanceof Blob) {
// Upload to server or create preview URL
const url = URL.createObjectURL(blob);
croppedImage.value = url;
}
};
// Get cropped base64 string
const handleCropBase64 = async () => {
const base64 = await cropperRef.value?.getCropImage('image/png', 1, 'base64');
if (typeof base64 === 'string') {
croppedImage.value = base64;
}
};
// Export with specific dimensions
const handleCropWithSize = async () => {
const blob = await cropperRef.value?.getCropImage(
'image/jpeg',
0.9,
'blob',
200, // target width
200, // target height
);
};
</script>
<template>
<div>
<VCropper
ref="cropperRef"
:img="imageUrl"
:width="500"
:height="400"
aspect-ratio="1:1"
/>
<button @click="handleCropBlob">Crop</button>
<img v-if="croppedImage" :src="croppedImage" />
</div>
</template>
```
Loading…
Cancel
Save