
【vue3图片裁剪】上传、裁剪、压缩图片尺寸
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue

·
vue3 上传图片及裁剪
vue3 + el-upload +VuePictureCropper
组件下载
npm install vue-picture-cropper
组件文档
https://cropper.chengpeiquan.com/zh/quick-start.html
弹窗:裁剪组件(ImgCorpper.vue)
<template>
<el-dialog v-model="isShowModal" title="Image Cropping" width="50%" :close-on-press-escape="false"
:close-on-click-modal="false" @close="cancel" align-center>
<div class="modal-content">
<VuePictureCropper :img="pic" @ready="ready" :boxStyle="corpBoxStyle" :options="corpOptions"
:presetMode="corpPresetMode" />
</div>
<div class="m-t-10 btns">
<el-button class="default-btn" @click="cancel">Cancel</el-button>
<el-button v-if="showClear" class="default-btn" @click="clear">Clear</el-button>
<el-button v-if="showReset" class="default-btn" @click="reset">Reset</el-button>
<el-button class="default-btn primary" @click="getResult">Confirm</el-button>
</div>
</el-dialog>
</template>
<script setup>
import { reactive, ref, watch, computed } from 'vue'
// 组件文档:https://cropper.chengpeiquan.com/zh/quick-start.html
import VuePictureCropper, { cropper } from 'vue-picture-cropper'
import { PhotoCompress } from "@utils/compressUtil.js"
const props = defineProps({
visible: {
type: Boolean
},
image: {
type: [String, File]
},
presetMode: {
type: Object
},
options: {
type: Object
},
boxStyle: {
type: Object
},
showClear: { // 是否显示 Clear 按钮
type: Boolean
},
showReset: { // 是否显示 Reset 按钮
type: Boolean
},
isCompress: { // 是否压缩裁剪后的图片大小
type: Boolean
},
compressSize: { // 压缩图片大小限制
type: Object,
default: {
maxSize: 0.3 * 1024 * 1024, // 300k
minSize: 0.03 * 1024 * 1024// 30k
}
}
})
/** 裁剪组件属性配置 */
const corpOptions = computed(() => {
return {
viewMode: 1, // 裁剪框范围 1-只能在图片区域内活动
dragMode: 'move', // 可选值 crop(可绘制裁剪框) | move(仅移动)
aspectRatio: 1 / 1,// 裁剪框的宽高比
cropBoxResizable: false, // 是否可以调整裁剪区的大小
...props.options
}
})
/** 裁剪区域的样式 */
const corpBoxStyle = computed(() => {
return {
width: 'auto',
height: '400px',
backgroundColor: '#f8f8f8',
margin: 'auto',
...props.boxStyle
}
})
/** 预设模式配置 */
const corpPresetMode = computed(() => {
return {
mode: 'fixedSize', width: 300, height: 300,
...props.presetMode
}
})
const emits = defineEmits(["close"])
watch(() => props.visible, (val) => {
isShowModal.value = val
if (!val) {
pic.value = ''
result.dataURL = ''
result.blobURL = ''
reset()
}
})
watch(() => props.image, (val) => {
pic.value = val
})
const isShowModal = ref(false)
const pic = ref('')
const result = reactive({
dataURL: '',
blobURL: '',
})
/**
* Get cropping results
*/
async function getResult() {
if (!cropper) return
const base64 = cropper.getDataURL()
const blob = await cropper.getBlob()
if (!blob) return
let file = await cropper.getFile({
fileName: "locales.fileName",
})
result.dataURL = base64
result.blobURL = URL.createObjectURL(blob)
const { minSize, maxSize } = props.compressSize
// console.log(file.size, maxSize)
// 是否限制文件大小
if (props.isCompress && (file.size > maxSize)) {
// console.log("compress")
const photoCompress = new PhotoCompress(minSize, maxSize);
photoCompress.compress(file, function (f) {
const r = new FileReader()
r.readAsDataURL(f)
r.onload = function (e) {
emits("close", { result, file: f })
}
});
} else {
// console.log("crop")
emits("close", { result, file })
}
}
/**
* Clear the crop box
*/
function clear() {
if (!cropper) return
cropper.clear()
}
/**
* Reset the default cropping area
*/
function reset() {
if (!cropper) return
cropper.reset()
}
/**
* The ready event passed to Cropper.js
*/
function ready() {
// console.log('Cropper is ready.')
}
function cancel() {
emits("close")
}
</script>
<style lang="scss" scoped>
.modal-wrap {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 99;
}
.btns {
display: flex;
justify-content: flex-end;
}
</style>
图片压缩方法(compressUtil.js)
/**
* 图片压缩类
* @param minSize
* @param maxSize
* @constructor
*/
export function PhotoCompress(minSize, maxSize) {
var nextQ = 0.5; // 压缩比例
var maxQ = 1;
var minQ = 0;
/**
* 将base64转换为文件
* @param base64Codes base64编码
* @param fileName 文件名称
* @returns {*}
*/
PhotoCompress.prototype.dataUrlToFile = function (base64Codes, fileName = new Date().getTime()) {
var arr = base64Codes.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bStr = atob(arr[1]),
n = bStr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bStr.charCodeAt(n);
}
return new File([u8arr], fileName, { type: mime });
}
/**
* 图片压缩
* @param file 文件
* @param callback 回调函数
*/
PhotoCompress.prototype.compress = function (file, callback) {
var self = this;
self.imgBase64(file, function (image, canvas) {
var base64Codes = canvas.toDataURL(file.type, nextQ); // y压缩
var compressFile = self.dataUrlToFile(base64Codes, file.name.split('.')[0]); // 转成file文件
var compressFileSize = compressFile.size; // 压缩后文件大小 k
console.log("图片质量:" + nextQ);
console.log("压缩后文件大小:" + compressFileSize / 1024);
if (compressFileSize > maxSize) { // 压缩后文件大于最大值
maxQ = nextQ;
nextQ = (nextQ + minQ) / 2; // 质量降低
self.compress(file, callback);
} else if (compressFileSize < minSize) { // 压缩以后文件小于最小值
minQ = nextQ;
nextQ = (nextQ + maxQ) / 2; // 质量提高
self.compress(file, callback);
} else {
callback(compressFile);
}
});
}
/**
* 将图片转化为base64
* @param file 文件
* @param callback 回调函数
*/
PhotoCompress.prototype.imgBase64 = function (file, callback) {
// 看支持不支持FileReader
if (!file || !window.FileReader) return;
var image = new Image();
// 绑定 load 事件处理器,加载完成后执行
image.onload = function () {
var canvas = document.createElement('canvas')
var ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = image.width * nextQ;
canvas.height = image.height * nextQ;
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
callback(image, canvas);
};
if (/^image/.test(file.type)) {
// 创建一个reader
var reader = new FileReader();
// 将图片将转成 base64 格式
reader.readAsDataURL(file);
// 读取成功后的回调
reader.onload = function () {
// self.imgUrls.push(this.result);
// 设置src属性,浏览器会自动加载。
// 记住必须先绑定事件,才能设置src属性,否则会出同步问题。
image.src = this.result;
}
}
}
};
组件使用
<template>
<el-form size="large" label-position="left">
<el-form-item prop="avatar" label="Avatar:" :required="true">
<el-upload list-type="picture-card" :auto-upload="true" :show-file-list="false" :before-upload="beforeUpload">
<img class="preview-img" v-if="formData.avatar" :src="formData.avatar" />
<el-icon v-else class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
</el-form-item>
<el-button class="default-btn" type="primary" @click="saveForm" size="default">Save</el-button>
</el-form>
<!-- 图片裁剪组件 -->
<ImgCorpper :visible="isShowCorpper" :image="selectPic" @close="hideCorpper" :isCompress="true"/>
</template>
<script setup>
import { ref } from "vue"
import ImgCorpper from '@components/ImgCorpper.vue'
let saveFile = ref()
let selectPic = ref("")
let isShowCorpper = ref(false)
/** 选择文件 */
function beforeUpload(file) {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
selectPic.value = String(reader.result)
isShowCorpper.value = true
}
return false
}
function hideCorpper(data) {
isShowCorpper.value = false
if (data) {
const { result: { dataURL }, file } = data
formData.value.avatar = dataURL;
saveFile.value = file
}
}
function saveForm() {
console.log(saveFile.value)
}
</script>
<style lang="scss" scoped>
.avatar-box {
width: 300px;
display: flex;
justify-content: center;
}
.preview-img {
width: 100%;
height: auto;
border-radius: 6px;
}
</style>




vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:6 个月前 )
9e887079
[skip ci] 4 个月前
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 8 个月前
更多推荐
所有评论(0)