Browse Source
- 新增中文文档 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
6 changed files with 479 additions and 0 deletions
@ -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> |
|||
``` |
|||
@ -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> |
|||
@ -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> |
|||
@ -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…
Reference in new issue