一、简介
Vue-Cropper 是一个基于 Vue.js 的轻量级图片裁剪组件,专为前端项目中的图片裁剪需求设计。它提供了丰富的功能和灵活的配置选项,支持多种图片格式输入和输出,适用于头像裁剪、商品图片处理、图片编辑等场景。
二、使用场景
用户头像上传:允许用户裁剪并上传符合要求的头像。
商品图片处理:电商平台中,用户可裁剪商品主图或详情图。
图片编辑工具:集成到图片编辑器中,提供基础裁剪功能。
三、集成 vue-cropper 插件
pnpm install vue-cropper@next
main.ts
import VueCropper from 'vue-cropper' // 图片裁剪
app.use(VueCropper) // 图片裁剪
四、封装组件
技术栈:pnpm、vue3、typescript、vue-cropper
<!-- 图片裁剪组件 -->
<template>
<el-dialog
title="提示"
v-model="dialogVisible"
width="80%"
:before-close="handleClose">
<div class="cropper-container">
<input type="file" accept="image/*" @change="handleFileChange" />
<vue-cropper
ref="cropperRef"
:img="option.img"
:outputSize="option.outputSize"
:outputType="option.outputType"
:info="option.info"
:canScale="option.canScale"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixed="option.fixed"
:fixedNumber="option.fixedNumber"
@realTime="realTime"
@imgLoad="imgLoad"
style="width: 100%; height: 400px;"
></vue-cropper>
<!-- 控制按钮 -->
<div class="btn-group">
<button @click="startCrop">开始裁剪</button>
<button @click="stopCrop">停止裁剪</button>
<button @click="clearCrop">清除裁剪</button>
<button @click="changeScale(1)">放大</button>
<button @click="changeScale(-1)">缩小</button>
<button @click="rotateLeft">左旋转</button>
<button @click="rotateRight">右旋转</button>
<button @click="getCropData">获取裁剪结果</button>
</div>
<!-- 实时预览 -->
<div class="preview-box">
<div class="preview" :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import image from '/m-general/assets/images/home.jpg'
const dialogVisible = ref(false) // 控制弹窗显示
const setDialogVisible = (val: boolean) => {
dialogVisible.value = val
}
defineExpose({ // 暴露给父组件
setDialogVisible
})
const handleClose = (done: Function) => {
done()
}
const option:any = reactive({
img: image, // 裁剪图片的地址
outputSize: 1, // 裁剪生成图片的质量(0.1-1)
outputType: 'jpeg', // 裁剪生成图片的格式
info: true, // 显示裁剪框的大小信息
canScale: true, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 300, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度
fixed: true, // 是否开启截图框宽高固定比例
fixedNumber: [3, 2], // 截图框的宽高比例
})
let previews:any = reactive({
url: image, // 图片地址
img: { // 裁剪后图片的样式
width: '100%',
height: '100%',
},
})
// 处理文件选择
const handleFileChange = (e: Event) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (!file) return
// 检查文件类型
if (!file.type.includes('image/')) {
alert('请选择图片文件')
return
}
// 创建URL
const reader = new FileReader()
reader.onload = (event) => {
console.log('77-', event)
option.img = event.target?.result as string
}
reader.readAsDataURL(file)
}
// 实时预览
const realTime = (data: any) => {
console.log('84--图片加载:', data)
previews.url = data.url
}
// 图片加载完成
const imgLoad = (msg: any) => {
console.log('图片加载:', msg)
}
const cropperRef = ref<InstanceType<typeof VueCropper> | null>(null)
// 开始裁剪
const startCrop = () =>{
if (!cropperRef.value) return
cropperRef.value.startCrop()
}
// 停止裁剪
const stopCrop = () => {
cropperRef.value.stopCrop()
}
// 清除裁剪
const clearCrop = () => {
cropperRef.value.clearCrop()
}
// 缩放
const changeScale = (num: number) => {
cropperRef.value.changeScale(num)
}
// 左旋转
const rotateLeft = () => {
cropperRef.value.rotateLeft()
}
// 右旋转
const rotateRight = () => {
cropperRef.value.rotateRight()
}
const emit = defineEmits([ // 定义事件
'setImage'
])
// 获取裁剪结果
const getCropData = () => {
// 获取base64数据
cropperRef.value.getCropData((data: string) => {
console.log('裁剪完成:', data)
previews.url = data
emit('setImage', data)
})
}
</script>
<style scoped lang="scss">
.cropper-container {
max-width: 800px;
margin: 0 auto;
.btn-group {
margin: 20px 0;
text-align: center;
}
.btn-group button {
margin: 0 5px;
padding: 8px 16px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-group button:hover {
background: #0056b3;
}
.preview-box {
margin-top: 20px;
}
.preview {
width: 200px;
height: 133px;
overflow: hidden;
border: 1px solid #ccc;
margin: 0 auto;
}
}
</style>
五、父组件调用
<template>
<el-button @click="btn">上传裁剪</el-button>
<m-cropper ref="myCropperRef" @setImage="setImage"></m-cropper>
<img :src="previews.url" :style="previews.img">
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
const myCropperRef = ref<InstanceType<typeof VueCropper> | null>(null)
const btn = async () => {
console.log('8--', myCropperRef.value)
myCropperRef.value.setDialogVisible(true)
}
let previews:any = reactive({
url: '', // 图片地址
img: { // 裁剪后图片的样式
width: '100px',
height: '100px',
},
})
const setImage = (val:any) => {
console.log('24--', val)
previews.url = val
}
</script>
<style scoped>
</style>
六、Vue-Cropper 部分核心配置项
配置项 | 说明 | 默认值 | 可选值 |
---|---|---|---|
img | 图片源,支持 URL、base64 或 blob 格式 | null | URL/base64/blob |
outputSize | 输出图片质量 | 1 | 0.1 - 1 |
outputType | 输出图片格式 | 'jpeg' | 'jpeg' 、'png' 、'webp' |
autoCrop | 是否自动生成裁剪框 | false | true 、false |
autoCropWidth | 裁剪框宽度 | 父容器的 80% | 数值 |
autoCropHeight | 裁剪框高度 | 父容器的 80% | 数值 |
fixed | 是否固定宽高比 | true | true 、false |
fixedNumber | 宽高比例 | [1, 1] | [width, height] |
canScale | 是否允许缩放图片 | true | true 、false |
canMove | 是否允许移动图片 | true | true 、false |
canMoveBox | 是否允许移动裁剪框 | true | true 、false |
fixedBox | 是否固定裁剪框大小 | false | true 、false |
mode | 图片渲染模式 | 'contain' | 'contain' 、'cover' 等 |
enlarge | 输出放大倍数 | 1 | 数值 |
centerBox | 是否限制裁剪框在图片内 | false | true 、false |
info | 是否显示裁剪框的大小信息 | false | true 、false |
full | 是否输出原图比例截图 | false | true 、false |
original | 上传图片是否显示原始宽高 | false | true 、false |
maxImgSize | 限制图片最大尺寸,避免移动端内存问题 | 无 | 数值(单位:像素) |
enableExif | 是否自动处理 EXIF 方向信息(旧版浏览器) | true | true 、false |
high | 是否开启高清模式 | false | true 、false |
crossOrigin | 图片跨域属性 | null | 'Anonymous' 、'Use-Credentials' 等 |
checkCrossOrigin | 是否检查跨域 | true | true 、false |
七、Vue-Cropper 的部分核心事件
事件名 | 说明 | 触发时机 |
---|---|---|
@realTime | 实时预览事件,在裁剪框移动或缩放时触发 | 裁剪框位置或大小变化时 |
@imgLoad | 图片加载完成事件 | 图片成功加载到裁剪组件后 |
@cropMove | 裁剪框移动事件 | 裁剪框在图片上移动时 |
@cropMoving | 裁剪框移动中事件(持续触发) | 裁剪框移动过程中持续触发 |
@zoom | 图片缩放事件 | 图片被缩放时 |
@crop | 裁剪完成事件(部分版本可能支持) | 裁剪操作完成时(需结合具体版本) |
@mouseMove.native | 鼠标移动事件(需结合原生事件修饰符,非插件内置但常用) | 鼠标在裁剪区域移动时 |
@mouseUp.native | 鼠标释放事件(需结合原生事件修饰符,非插件内置但用于结束裁剪交互) | 鼠标在裁剪区域释放时 |