上传图片裁剪


安装 vue-croppe

复制代码
<el-row :gutter="20">
                    <el-col :span="12">
                        <div>
                            <label class="form-label">封面</label><span
                                class="form-required">jpeg、png格式,建议尺寸:372x208,大小<1M</span>
                        </div>
                        <input type="file" ref="fileInput" accept="image/png,image/jpeg" @change="handleFileSelect"
                            class="file-input" style="display: none;" />
                        <div class="resourceLibrary-upload-edit" @click="$refs.fileInput.click()">
                            <img v-if="form.chapterCover" :src="form.chapterCoverImg" alt="">
                            <div :class="{ 'upload-edit-icon-container': form.chapterCover }">
                                 <div v-if="!form.chapterCover">
                                    <el-icon class="upload-edit-icon">
                                        <upload-filled />
                                    </el-icon>
                                    <p class="rupload-edit">选择文件</p>
                                </div>
                                <p v-else class="rupload-edit-icon-text">选择文件</p>
                            </div>
                        </div>
                    </el-col>

                </el-row>

const isShowCropper = ref<boolean>(false)
const userCropperRef = ref()
const handleFileSelect = (e: any) => {
    const file = e.target.files[0];
    if (!file) {
        ElMessage.warning("请选择图片");
        return;
    }
    if (!file.type.match(/image\/(png|jpg|jpeg)/)) {
        ElMessage.warning("请选择png、jpg或jpeg格式图片文件");
        return;
    }

    if (file.size > 1 * 1024 * 1024) {
        ElMessage.warning('上传文件不能大于1M')
        return
    }
    if (file) {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = (event) => {
            // 获取图片的宽度和高度
            const result = event.target.result;
            isShowCropper.value = true;
            nextTick(() => {
                userCropperRef.value.setPreview(result);
            })

        }
    }
}
const handleConfirm = (val) => {
    form.value.chapterCoverImg =  val;
    form.value.chapterCover = val;
    isShowCropper.value = false;
}

组件

复制代码
 <user-cropper ref="userCropperRef" v-if="isShowCropper" :title="'上传封面'" :wh="{
        width: 372,
        height: 208
    }" :src="form.resourceUrl" @confirm="handleConfirm" />

<template>
    <el-dialog :title="props.title" v-model="dialogVisible" width="600px">
        <div class="box_cropper">
            <vueCropper ref="cropperRef" :img="option.img" :outputSize="option.outputSize"
                :outputType="option.outputType" :canScale='option.canScale' :autoCrop='option.autoCrop'
                :autoCropWidth='option.autoCropWidth' :autoCropHeight='option.autoCropHeight'
                :canMoveBox='option.canMoveBox' :canMove='option.canMove' :centerBox='option.centerBox'
                :info='option.info' :fixedBox='option.fixedBox' :width='option.width' :height='option.height' :mode='option.mode'>
            </vueCropper>
        </div>
        <template #footer>
            <el-button type="primary" @click="handleConfirm">确认</el-button>
        </template>
    </el-dialog>
</template>

<script setup lang="ts">
import { ref, nextTick } from 'vue'
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'
import { uploadFile, deleteFile } from '@/api/file'
import { base64ImgtoFile } from '@/utils/index'
const dialogVisible = ref<boolean>(false)
const props = defineProps({
    title: {
        type: String,
        default: '上传头像'
    },
    wh: {
        type: Object,
        default: () => ({
            width: 300,
            height: 300
        })
    },
    cropperHeight: {
        type: Number,
        default: 0
    },
    cropperWidth: {
        type: Number,
        default: 0
    }
})
const cropperRef = ref('')
const cropper_width = ref(0) // 真实的输出图片宽度
const cropper_height = ref(0) // 真实的输出图片高度
// const cropperHeight = ref(props.wh.height+120);
const option = ref({
    img: '',
    outputType: 'png',
    canScale: true,
    autoCrop: true,
    info: true,
    autoCropWidth: props.wh.width,
    autoCropHeight: props.wh.height,
    canMoveBox: true,
    fixedBox: true,
    canMove: true,
    enlarge: 2,
    full: true,
    fixed: true,
    original: false,
    centerBox: false,
    infoTrue: true,
   
})
const emit = defineEmits(['confirm'])
 const setPreview = (img: string) => {
        dialogVisible.value = true
        nextTick(() => {
            // 1. 重置裁剪框尺寸
            option.value.autoCropWidth = props.wh.width
            option.value.autoCropHeight = props.wh.height

            // 2. 获取图片尺寸进行计算
            const imgData = new Image()
            imgData.src = img
            imgData.onload = () => {
                const cropRatio = props.wh.width / props.wh.height // 裁剪框比例
                const imgRatio = imgData.width / imgData.height    // 图片比例

                let modeWidth, modeHeight;

                if (imgRatio > cropRatio) {
                    // 图片比例 > 裁剪框比例 (图片更宽)
                    // --- 以宽度为基准 ---
                    modeWidth = imgData.width;
                    // 计算高度:根据裁剪框宽度计算图片应有的高度
                    modeHeight = props.wh.height;
                } else {
                    // 图片比例 <= 裁剪框比例 (图片更高或等比)
                    // --- 以高度为基准 ---
                    modeHeight = imgData.height;
                    // 计算宽度:根据裁剪框高度计算图片应有的宽度
                    modeWidth = props.wh.width;
                }
                console.log(modeWidth, modeHeight)
                option.value.mode = `${modeWidth}px ${modeHeight}px`
                option.value.width = modeWidth
                option.value.height = modeHeight
                // 图片缩放到裁剪框尺寸,不能在缩放
                option.value.canScale = false
                option.value.img = img; 
            }
        })
    }

const handleConfirm = () => {
    cropperRef.value.getCropData(async (data) => {
        // 获取真实输出的图片宽高
        const img = new Image()
        img.onload = () => {
            cropper_width.value = img.width
            cropper_height.value = img.height
        }
        // 将base64转换为PNG格式的File对象
        const file = base64ImgtoFile(data)
        // 上传文件
        const { data: fileInfo } = await uploadFile(file)
        // 将上传后的图片URL传递回父组件
        emit('confirm', fileInfo.relativeUrl)
    })
}

defineExpose({
    setPreview
})
</script>
<style scoped lang="scss">
.box_cropper {
    border: 10px solid white;
    // width: 100%;
    height: 400px;
}
</style>

相关代码

···

export const base64ImgtoFile = (base64Data: string) => {

// 获取base64数据部分,去掉前缀

let byteCharacters = ''

let byteNumbers = ''

const base64String = base64Data.split(',')[1]

byteCharacters = atob(base64String)

byteNumbers = new Array(byteCharacters.length)

for (let i = 0; i < byteCharacters.length; i++) {

byteNumbers[i] = byteCharacters.charCodeAt(i)

}

const byteArray = new Uint8Array(byteNumbers)

// 创建PNG格式的Blob对象

const blob = new Blob([byteArray], { type: 'image/png' })

// 转换为File对象,添加.png扩展名

const fileName = cropped_img${Date.now()}.png

const file = new File([blob], fileName, { type: 'image/png' })

return file

}

···

相关推荐
大闲在人2 小时前
C、C++区别还是蛮大的
c语言·开发语言·c++
光影少年3 小时前
浏览器渲染原理?
前端·javascript·前端框架
小白探索世界欧耶!~3 小时前
Vue2项目引入sortablejs实现表格行拖曳排序
前端·javascript·vue.js·经验分享·elementui·html·echarts
Cosmoshhhyyy3 小时前
《Effective Java》解读第39条:注解优先于命名模式
java·开发语言
清水白石0084 小时前
Python 纯函数编程:从理念到实战的完整指南
开发语言·python
掘根4 小时前
【C++STL】平衡二叉树(AVL树)
开发语言·数据结构·c++
叫我一声阿雷吧4 小时前
JS实现响应式导航栏(移动端汉堡菜单)|适配多端+无缝交互【附完整源码】
开发语言·javascript·交互
前路不黑暗@4 小时前
Java项目:Java脚手架项目的文件服务(八)
java·开发语言·spring boot·学习·spring cloud·docker·maven
毅炼5 小时前
Java 集合常见问题总结(3)
java·开发语言·后端