vue-cropper实现图片裁剪
一、什么是vue-cropper?
Vue-Cropper 是一个基于 Vue.js 的图片裁剪组件库,专为 Web 应用设计。当你在网上搜索的时候发现还有一个叫cropper的库,下面是他们的区别:
特性 | cropper.js | vue-cropper |
---|---|---|
框架依赖 | 纯 JavaScript,无框架依赖 | 专为 Vue.js 设计 |
包体积 | ~200 KB (含样式) | ~45 KB (压缩后) |
API 调用方式 | 原生 DOM 操作 | Vue 组件式 API |
响应式支持 | 需手动实现 | 原生支持 Vue 响应式 |
功能完整性 | 基础裁剪+扩展插件 | 基础裁剪+常用扩展 |
学习曲线 | 较高(需熟悉原生 API) | 较低(Vue 开发者友好) |
社区生态 | GitHub 32k+ Star | GitHub 3.5k+ Star |
移动端支持 | 需额外配置 | 原生优化 |
vue-cropper的优势:
• 纯前端实现,减轻服务器压力
• 响应式设计,完美适配移动端
• 灵活的配置选项
• 实时预览功能
• 支持多种输出格式
我正在开发的项目是基于vue3的,所以就对于vue-cropper进行讲解
二、快速上手
安装
//npm 安装
npm install vue-cropper
//yarn 安装
yarn add vue-cropper
引入
//Vue 3 组件内引入npm install vue-cropper@next
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper";
//Vue3 全局引入import VueCropper from 'vue-cropper';
import 'vue-cropper/dist/index.css'const app = createApp(App)
app.use(VueCropper)
app.mount('#app')
示例代码
<template><div class="container"><input type="file" @change="handleFileChange" /><vue-cropperref="cropper":img="imageSrc":auto-crop="false":aspect-ratio="16/9"/><button @click="getResult">获取裁剪结果</button></div>
</template><script setup>
import { ref } from 'vue'const imageSrc = ref('')
const cropper = ref(null)const handleFileChange = (e) => {const file = e.target.files[0]const reader = new FileReader()reader.onload = (e) => {imageSrc.value = e.target.result}reader.readAsDataURL(file)
}const getResult = () => {cropper.value.getCropBlob(blob => {const formData = new FormData()formData.append('file', blob)// 上传逻辑...})
}
</script>
三、配置详解
基础配置项
const options = reactive({img: '', // 图片源地址outputSize: 1, // 输出质量(0-1)outputType: 'png',// 输出格式:png | jpeg | webpinfo: true, // 显示裁剪信息canScale: true, // 允许缩放autoCrop: true, // 自动生成裁剪框fixed: false, // 固定裁剪框尺寸fixedNumber: [4, 3], // 固定比例 [宽, 高]full: false // 是否全屏模式
})
比例锁定实践
固定16:9的案例:
<vue-cropper:fixed="true":fixed-number="[16, 9]":img="imageSrc"@realTime="handlePreview"
/>
输出格式控制
const outputOptions = {types: ['png', 'jpeg', 'webp'],quality: {jpeg: 0.8,webp: 0.7}
}const handleExport = (type) => {cropper.value.getCropData(type, (dataURL) => {const link = document.createElement('a')link.download = `cropped.${type}`link.href = dataURLlink.click()})
}
四、案例
用户头像上传组件
<template><div class="avatar-editor"><n-uploadaccept="image/*":before-upload="handleUpload"><n-button>选择头像</n-button></n-upload><n-modal v-model:show="showCropper"><n-card style="width: 600px"><vue-cropperref="cropper":img="tempImage":fixed="true":fixed-number="[1, 1]":output-size="0.8"/><div class="action-buttons"><n-button @click="rotate(-90)">左旋转</n-button><n-button @click="rotate(90)">右旋转</n-button><n-button @click="saveAvatar">保存</n-button></div></n-card></n-modal></div>
</template><script setup>
// 完整业务逻辑
const showCropper = ref(false)
const tempImage = ref('')const handleUpload = async (file) => {const reader = new FileReader()reader.onload = (e) => {tempImage.value = e.target.resultshowCropper.value = true}reader.readAsDataURL(file)return false
}const rotate = (degree) => {cropper.value.rotate(degree)
}const saveAvatar = async () => {cropper.value.getCropBlob(async (blob) => {const formData = new FormData()formData.append('avatar', blob)try {await axios.post('/api/upload-avatar', formData)message.success('头像更新成功')showCropper.value = false} catch (error) {message.error('上传失败')}})
}
</script>
五、扩展
主题自定义
/* 自定义主题 */
.vue-cropper {--crop-border: 2px dashed #f00;--preview-bg: #f8f9fa;.crop-box {box-shadow: 0 0 10px rgba(0,0,0,0.5);}.info-item {color: #1890ff;font-size: 12px;}
}
性能优化技巧
// 压缩处理函数
const optimizeImage = (blob) => {return new Promise((resolve) => {const img = new Image()img.onload = () => {const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')canvas.width = 800canvas.height = (800 * img.height) / img.widthctx.drawImage(img, 0, 0, canvas.width, canvas.height)canvas.toBlob(resolve, 'image/webp', 0.8)}img.src = URL.createObjectURL(blob)})
}
移动端适配方案
<vue-cropper:touch="true":movable="false":zoom-on-touch="true":zoom-on-wheel="false"
/>
/* 移动端优化 */
@media (max-width: 768px) {.vue-cropper {--crop-box-width: 300px;--crop-box-height: 300px;.action-buttons {flex-direction: column;button {margin: 5px 0;}}}
}
六、常见问题
裁剪框比例不生效?
解决方案:
// 确保同时配置
fixed: true,
fixedNumber: [3, 2],
aspectRatio: 3/2
图片显示变形?
处理方案:
const checkImage = (file) => {return new Promise((resolve) => {const img = new Image()img.onload = () => {const ratio = img.width / img.heightif (Math.abs(ratio - 3/2) > 0.1) {message.warning('建议选择3:2比例的图片')}resolve(true)}img.src = URL.createObjectURL(file)})
}
七、props
以下是全部的参数以及取值要求
名称 | 功能 | 默认值 | 可选值 |
---|---|---|---|
img | 裁剪图片的地址 | 空 | url 地址, base64, blob |
outputSize | 裁剪生成图片的质量 | 1 | 0.1 ~ 1 |
outputType | 裁剪生成图片的格式 | jpg (需传入 jpeg) | jpeg, png, webp |
info | 裁剪框的大小信息 | true | true, false |
canScale | 图片是否允许滚轮缩放 | true | true, false |
autoCrop | 是否默认生成截图框 | false | true, false |
autoCropWidth | 默认生成截图框宽度 | 容器的 80% | 0 ~ max |
autoCropHeight | 默认生成截图框高度 | 容器的 80% | 0 ~ max |
fixed | 是否开启截图框宽高固定比例 | false | true, false |
fixedNumber | 截图框的宽高比例(开启 fixed 生效) | [1, 1] | [ 宽度 , 高度 ] |
full | 是否输出原图比例的截图 | false | true, false |
fixedBox | 固定截图框大小 | 不允许改变 | true, false |
canMove | 上传图片是否可以移动 | true | true, false |
canMoveBox | 截图框能否拖动 | true | true, false |
original | 上传图片按照原始比例渲染 | false | true, false |
centerBox | 截图框是否被限制在图片里面 | false | true, false |
high | 是否按照设备的 dpr 输出等比例图片 | true | true, false |
infoTrue | 展示真实输出图片宽高 | false | true, false |
maxImgSize | 限制图片最大宽度和高度 | 2000 | 0 ~ max |
enlarge | 图片根据截图框输出比例倍数 | 1 | 0 ~ max(建议不要太大) |
mode | 图片默认渲染方式 | contain | contain, cover, 100px, 100% auto |
limitMinSize | 裁剪框限制最小区域 | 10 | Number, Array, String |
fillColor | 导出时背景颜色填充 | 空 | #ffffff, white |