vue3和ts和vue-cropper 实现图片裁剪预览

​​Vue-Cropper​​ 是一个基于 Vue.js 的图片裁剪组件库,专为 Web 应用设计。当你在网上搜索的时候发现还有一个叫cropper的库,下面是他们的区别:

二、快速上手

复制代码
//npm 安装
npm install vue-cropper
//yarn 安装
yarn add vue-cropper

对该插件进行封装,使用vue3 + ts

复制代码
<script lang="ts" setup>
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'

const emit = defineEmits(['realTime'])
const props = defineProps({
    width: { type: Number, default: 640 },
    height: { type: Number, default: 740 }
})

const options = reactive({
    img: '',
    autoCrop: true,
    autoCropWidth: 640,
    autoCropHeight: 740,
    fillColor: '#fff', // 裁剪框填充颜色
    fixedBox: true //  固定裁剪框
})

const cropperInstance = ref()
const blobData = ref(new Blob())

const init = () => {
    options.img = ''
}

const selectImg = async (e: Event) => {
    const file = (e.target as HTMLInputElement).files?.[0]
    if (!file) return

    const reader = new FileReader()
    reader.onload = async e => {
        const img = new Image()
        img.onload = async () => {
            // 强制放大到最小尺寸
            const canvas = document.createElement('canvas')
            const ctx = canvas.getContext('2d')!

            // 设置 canvas 尺寸
            canvas.width = img.width
            canvas.height = img.height
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
            options.img = canvas.toDataURL()
            await nextTick()
            // 等待 DOM 更新后设置裁剪框
            setFixedCropBox()
        }
        img.src = e.target!.result as string
    }
    reader.readAsDataURL(file)
}

const setFixedCropBox = () => {
    if (!cropperInstance.value?.cropper) return

    // 正确访问cropper实例
    const cropper = cropperInstance.value.cropper

    // 获取容器真实尺寸
    const containerWidth = cropper.containerData.width
    const containerHeight = cropper.containerData.height

    // 计算居中位置
    const left = (containerWidth - props.width) / 2
    const top = (containerHeight - props.height) / 2

    // 正确调用方法
    cropper.setCropBoxData({
        width: props.width,
        height: props.height,
        left,
        top
    })
}

// 实时预览
const realTime = (data: any) => {
    emit('realTime', data.html)
}

// 获取截图的数据 getCropBlob => blob数据
const tailoring = (): Promise<Blob> => {
    return new Promise((resolve, reject) => {
        if (!cropperInstance.value) {
            reject(new Error('裁剪组件未初始化'))
            return
        }

        cropperInstance.value.getCropBlob((data: Blob) => {
            if (data) {
                blobData.value = data
                resolve(data)
            } else {
                reject(new Error('裁剪失败,未获取到 Blob'))
            }
        })
    })
}

// 旋转
const rotateLeft = () => {
    if (!cropperInstance.value) return
    cropperInstance.value.rotateLeft()
}

const rotateRight = () => {
    if (!cropperInstance.value) return
    cropperInstance.value.rotateRight()
}

onMounted(() => {
    if (props.width && props.height) {
        options.autoCropWidth = props.width
        options.autoCropHeight = props.height
    }
})

defineExpose({ options, selectImg, realTime, tailoring, blobData, init, rotateLeft, rotateRight })
</script>

<template>
    <vue-cropper
        ref="cropperInstance"
        :img="options.img"
        :autoCrop="options.autoCrop"
        :autoCropWidth="options.autoCropWidth"
        :autoCropHeight="options.autoCropHeight"
        :fixedBox="options.fixedBox"
        :fillColor="options.fillColor"
        @real-time="realTime"
    />
</template>

父组件进行使用

<el-form-item>
<div class="flex items-center gap-10px">
<span>公告图片</span>
<!-- <el-button size="small" type="primary" class="mx-0" @click="openUpload">
<el-icon class="mr-3px"><Upload /></el-icon> 上传</el-button
> -->
<el-button :type="form.backgroundImage ? 'success' : 'primary'" @click="openUpload">
<el-icon class="mr-3px"><Upload /></el-icon>
{{ form.backgroundImage ? '重新上传' : '上传' }}</el-button
>
<el-button class="mx-0" type="danger" :icon="Delete" @click="form.backgroundImage = ''"></el-button>
<div class="text-slate text-xs">建议:宽高640*740,png或jpg/jpeg格式,小于2M</div>
</div>
<input type="file" name="" ref="uploadRef" class="hidden" @change="handleCaptureUpload" />
</el-form-item>

<!-- 图片预览裁剪 -->
<el-dialog
v-model="showCaptureVisible"
title="图片预览裁剪"
width="50%"
@close="closeCapture"
top="5vh"
draggable
destroy-on-close
>
<div class="w-full h-78vh mt-10px flex justify-center items-center">
<Cropper ref="cropper" :height="740" :width="640"></Cropper>
</div>
<template #footer>
<span class="dialog-footer">
<el-button :icon="RefreshRight" type="info" @click="handleRotateRight"></el-button>
<el-button :icon="RefreshLeft" type="info" @click="handleRotateLeft"></el-button>
<el-button type="info" @click="closeCapture">关闭</el-button>
<el-button type="primary" @click="tailoring" :loading="cropperLoading">确定</el-button>
</span>
</template>
</el-dialog>

<script setup lang="ts">
const uploadRef = ref()
const showCaptureVisible = ref(false)
const cropper = ref()
// 上传图片loading
const { loading: cropperLoading } = useLoading()
// 裁剪图片
const tailoring = async () => {
//文件大小
cropperLoading.value = true
const blob = await cropper.value.tailoring()
if (blob.size > 2 * 1024 * 1024) {
cropperLoading.value = false
warningMessage('图片大小超出2M限制')
return
}

const formData = new FormData()
formData.append('file', blob, 'cropped.jpg')
formData.append('accountId', regionSelect.brandId as string)
uploadImage(formData) // 这里调用后台api
.then(
async res => {
form.value.backgroundImage = res.data
doSuccess('上传成功')
},
() => {
ElNotification({
type: 'warning',
message: '图片上传失败'
})
}
)
.finally(() => {
formRef.value?.validate().catch(() => {})
// 关闭裁剪
showCaptureVisible.value = false
cropperLoading.value = false
})
}

// 图片旋转
const handleRotateLeft = () => {
cropper.value?.rotateLeft()
}

const handleRotateRight = () => {
cropper.value?.rotateRight()
}
// 上传图片
const handleCaptureUpload = (e: any) => {
const input = e.target as HTMLInputElement
if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
warningMessage('图片类型要求:jpeg、jpg、png')
return false
}
showCaptureVisible.value = true
nextTick(() => {
cropper.value?.selectImg(e)
input.value = ''
})
}
// 关闭裁剪
const closeCapture = () => {
showCaptureVisible.value = false
}
</script>

效果图:

可进行图片旋转,裁剪 自定义裁剪的图片大小

三.vue-cropper的全部参数