基于 Vue3 实现上传图片裁剪功能
项目概述
图片上传时可以实现裁剪功能。
相关知识点
- vue-cropper 插件
实现
实现思路
- 引入
vue-cropper
插件 - 封装
vue-cropper
组件,CropperModal
- 封装 Upload 组件,并在
Upload
组件中引入CropperModal
组件 - 在
beforeUpload
方法中控制CropperModal
组件显隐
引入 vue-cropper
插件
安装
npm install vue-cropper
npm install vue-cropper@next
样式引入
import 'vue-cropper/dist/index.css'
CropperModal
组件封装
<script setup>
import {ref} from 'vue'
import {VueCropper} from 'vue-cropper'
const cropper = ref()
const option = ref({
img: 'https://avatars2.githubusercontent.com/u/15681693?s=460&v=4',
// img: '',
size: 1,
full: false,
outputType: 'png',
canMove: true,
fixedBox: true,
fixed: true,
original: false,
canMoveBox: true,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 750,
autoCropHeight: 340,
centerBox: true,
high: true,
fixedNumber: [1, 1],
max: 99999,
})
const previews = ref({})
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
})
const emits = defineEmits(['update:visible'])
const handleOk = () => {
cropper.value.getCropBlob((data) => {
console.log('截图数据 >>> ', data);
fetchUpLoad(data)
});
}
const handleCancel = (e) => {
emits('update:visible', false)
};
</script>
<template>
<a-modal v-model:open="props.visible" @ok="handleOk" @cancel="handleCancel" :closable="false">
<div class="cut">
<VueCropper
ref="cropper"
:img="option.img"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:full="option.full"
:fixed="option.fixed"
:fixed-number="option.fixedNumber"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:fixed-box="option.fixedBox"
:original="option.original"
:auto-crop="option.autoCrop"
:auto-crop-width="option.autoCropWidth"
:auto-crop-height="option.autoCropHeight"
:center-box="option.centerBox"
:high="option.high"
mode="cover"
:max-img-size="option.max"
/>
</div>
</a-modal>
</template>
<style scoped lang="less">
.cut {
height: 300px;
margin: 30px auto;
}
</style>
Upload
组件封装
这里以 Antd - upload
为例。
<script setup>
import { ref } from 'vue';
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { upLoad } from '@/api/httpApi'
import CropperModal from "@/components/CropperModal.vue";
const fileList = ref([]);
const loading = ref(false);
const imageUrl = ref('');
const modalVisible = ref(false);
const beforeUpload = (file) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
};
const handleUpload = async params => {
console.log('file >>> ', file);
const ObjectURL = URL.createObjectURL(params.file);
console.log('ObjectURL >>> ', ObjectURL);
const formData = new FormData();
formData.append('multipartFile', params);
const res = await upLoad(formData)
imageUrl.value = res?.filePreviewPathFull
}
</script>
<template>
<a-button type="primary" @click="modalVisible = true">modal</a-button>
<a-upload
v-model:file-list="fileList"
name="avatar"
list-type="picture-card"
class="avatar-uploader"
:show-upload-list="false"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
:before-upload="beforeUpload"
:customRequest="handleUpload"
>
<a-image
v-if="imageUrl"
:width="200"
:src="imageUrl"
/>
<div v-else>
<loading-outlined v-if="loading"></loading-outlined>
<plus-outlined v-else></plus-outlined>
<div class="ant-upload-text">Upload</div>
</div>
<CropperModal v-model:visible="modalVisible" />
</a-upload>
</template>
<style lang="less" scoped>
.avatar-uploader > .ant-upload {
width: 128px;
height: 128px;
}
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
</style>
这里要注意一下,objectURL = URL.createObjectURL(object);
在使用时要确保项目运行环境浏览器版本是否可用。若不确定可以使用粗暴大法 - 先将原来的图片上传,拿到 url 地址,再将其裁剪后再做一次上传。当然了,这种方法肯定是不推荐的,但是暂时想不到什么更优雅的方法。
若有小伙伴有更加优雅且兼容性更好地方法,欢迎评论讨论。
相关文档
Props
名称 | 功能 | 默认值 | 可选值 |
---|---|---|---|
img | 裁剪图片的地址 | 空 | url 地址 , base64 , blob |
outputSize | 裁剪生成图片的质量 | 1 | 0.1 ~ 1 |
outputType | 裁剪生成图片的格式 | jpg (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 | 截图框的宽高比例 | [1, 1] | [ 宽度 , 高度 ] |
full | 是否输出原图比例的截图 | false | true , false |
fixedBox | 固定截图框大小 | 不允许改变 | false |
canMove | 上传图片是否可以移动 | true | true , false |
canMoveBox | 截图框能否拖动 | true | true , false |
original | 上传图片按照原始比例渲染 | false | true , false |
centerBox | 截图框是否被限制在图片里面 | false | true , false |
high | 是否按照设备的dpr 输出等比例图片 | true | true , false |
infoTrue | true 为展示真实输出图片宽高 false 展示看到的截图框宽高 | 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 |
回调方法
@realTime
实时预览事件@imgMoving
图片移动回调函数@cropMoving
截图框移动回调函数@imgLoad
图片加载的回调, 返回结果success
,error
内置方法
方法 | 说明 |
---|---|
this.$refs.cropper.startCrop() | 开始截图 |
this.$refs.cropper.stopCrop() | 停止截图 |
this.$refs.cropper.clearCrop() | 清除截图 |
this.$refs.cropper.changeScale() | 修改图片大小 正数为变大 负数变小 |
this.$refs.cropper.getImgAxis() | 获取图片基于容器的坐标点 |
this.$refs.cropper.getCropAxis() | 获取截图框基于容器的坐标点 |
this.$refs.cropper.goAutoCrop | 自动生成截图框函数 |
this.$refs.cropper.rotateRight() | 向右边旋转90度 |
this.$refs.cropper.rotateLeft() | 向左边旋转90度 |
以上。
转载自:https://juejin.cn/post/7267090979537223735