图片裁剪示例
使用 Vue Cropper 实现图片裁剪和上传功能。
功能特性
- ✂️ 图片裁剪 - 支持自由裁剪、固定比例裁剪
- 🔄 旋转翻转 - 支持图片旋转和翻转
- 🔍 缩放调整 - 支持图片缩放
- 📤 实时预览 - 实时预览裁剪效果
安装依赖
bash
npm install vue-cropper基础用法
vue
<template>
<div class="cropper-container">
<vue-cropper
ref="cropperRef"
:img="imageUrl"
:output-size="1"
:output-type="'jpeg'"
:info="true"
:can-scale="true"
:auto-crop="true"
:auto-crop-width="200"
:auto-crop-height="200"
:fixed="true"
:fixed-number="[1, 1]"
/>
<div class="actions">
<el-button @click="rotateLeft">向左旋转</el-button>
<el-button @click="rotateRight">向右旋转</el-button>
<el-button @click="getCropBlob">获取裁剪结果</el-button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VueCropper } from 'vue-cropper'
const cropperRef = ref()
const imageUrl = ref('https://example.com/image.jpg')
const rotateLeft = () => {
cropperRef.value.rotateLeft()
}
const rotateRight = () => {
cropperRef.value.rotateRight()
}
const getCropBlob = () => {
cropperRef.value.getCropBlob((blob) => {
// 处理裁剪后的图片
console.log(blob)
})
}
</script>完整示例
vue
<template>
<div class="page-container">
<div class="toolbar">
<h2 class="page-title">图片裁剪示例</h2>
<div class="actions">
<el-upload
accept="image/*"
:auto-upload="false"
:on-change="handleImageChange"
:show-file-list="false"
>
<el-button type="primary">选择图片</el-button>
</el-upload>
<el-button @click="rotateLeft">
<el-icon><RefreshLeft /></el-icon>向左旋转
</el-button>
<el-button @click="rotateRight">
<el-icon><RefreshRight /></el-icon>向右旋转
</el-button>
<el-button @click="scaleX">
水平翻转
</el-button>
<el-button @click="scaleY">
垂直翻转
</el-button>
<el-button type="success" @click="getCropData">
获取裁剪结果
</el-button>
</div>
</div>
<div class="cropper-wrapper">
<div class="cropper-box">
<vue-cropper
ref="cropperRef"
:img="imageUrl"
:output-size="1"
output-type="png"
:info="true"
:can-scale="true"
:auto-crop="true"
:auto-crop-width="300"
:auto-crop-height="300"
:fixed="fixed"
:fixed-number="fixedNumber"
:center-box="true"
@real-time="realTime"
/>
</div>
<div class="preview-box">
<h3>预览</h3>
<div class="preview-img" :style="previewStyle">
<img :src="previews.url" :style="previews.img">
</div>
<div class="options">
<el-form label-width="100px">
<el-form-item label="固定比例">
<el-switch v-model="fixed" />
</el-form-item>
<el-form-item label="比例" v-if="fixed">
<el-radio-group v-model="ratio">
<el-radio-button :label="[1, 1]">1:1</el-radio-button>
<el-radio-button :label="[16, 9]">16:9</el-radio-button>
<el-radio-button :label="[4, 3]">4:3</el-radio-button>
</el-radio-group>
</el-form-item>
</el-form>
</div>
</div>
</div>
<!-- 裁剪结果弹窗 -->
<el-dialog v-model="resultVisible" title="裁剪结果" width="400px">
<img :src="resultUrl" style="width: 100%;">
<template #footer>
<el-button @click="resultVisible = false">关闭</el-button>
<el-button type="primary" @click="uploadImage">上传</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { VueCropper } from 'vue-cropper'
import { RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
const cropperRef = ref()
const imageUrl = ref('')
const fixed = ref(true)
const ratio = ref([1, 1])
const resultVisible = ref(false)
const resultUrl = ref('')
const previews = ref({})
const fixedNumber = computed(() => {
return fixed.value ? ratio.value : undefined
})
const previewStyle = computed(() => {
return {
width: '300px',
height: `${300 / ratio.value[0] * ratio.value[1]}px`,
overflow: 'hidden'
}
})
const handleImageChange = (file) => {
const reader = new FileReader()
reader.onload = (e) => {
imageUrl.value = e.target.result
}
reader.readAsDataURL(file.raw)
}
const rotateLeft = () => {
cropperRef.value.rotateLeft()
}
const rotateRight = () => {
cropperRef.value.rotateRight()
}
const scaleX = () => {
cropperRef.value.scaleX(-1)
}
const scaleY = () => {
cropperRef.value.scaleY(-1)
}
const realTime = (data) => {
previews.value = data
}
const getCropData = () => {
cropperRef.value.getCropData((data) => {
resultUrl.value = data
resultVisible.value = true
})
}
const uploadImage = () => {
cropperRef.value.getCropBlob((blob) => {
// 创建 FormData 上传
const formData = new FormData()
formData.append('file', blob, 'cropped.png')
// 调用上传 API
// uploadApi(formData)
ElMessage.success('上传成功')
resultVisible.value = false
})
}
</script>
<style scoped>
.page-container {
padding: 20px;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.actions {
display: flex;
gap: 10px;
}
.cropper-wrapper {
display: flex;
gap: 20px;
}
.cropper-box {
flex: 1;
height: 500px;
background: #f5f5f5;
}
.preview-box {
width: 350px;
}
.preview-img {
background: #f5f5f5;
margin-bottom: 20px;
}
.options {
padding: 20px;
background: #f9f9f9;
border-radius: 4px;
}
</style>Props 配置
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| img | String | '' | 图片地址 |
| outputSize | Number | 1 | 输出图片质量 0-1 |
| outputType | String | 'jpeg' | 输出图片格式 |
| autoCrop | Boolean | true | 是否自动裁剪 |
| autoCropWidth | Number | 200 | 裁剪框宽度 |
| autoCropHeight | Number | 200 | 裁剪框高度 |
| fixed | Boolean | false | 是否固定比例 |
| fixedNumber | Array | [1, 1] | 比例 [宽, 高] |
| centerBox | Boolean | false | 裁剪框是否限制在图片内 |
| canScale | Boolean | true | 是否允许缩放 |
方法
javascript
// 向左旋转
cropperRef.value.rotateLeft()
// 向右旋转
cropperRef.value.rotateRight()
// 水平翻转
cropperRef.value.scaleX(-1)
// 垂直翻转
cropperRef.value.scaleY(-1)
// 获取 base64
cropperRef.value.getCropData((data) => {
console.log(data)
})
// 获取 blob
cropperRef.value.getCropBlob((blob) => {
console.log(blob)
})
// 获取裁剪后的图片文件
cropperRef.value.getCropFile((file) => {
console.log(file)
})