Vue3 实现拖拽截图功能

首先说下业务场景:

业务当中需要上传一张大图,然后在大图上面截取一小部分图片作为target图片,在截图的同时获取到截取图片区域对应在大图上的坐标区域。

如果你的vue版本时3.0以上,代码可直接使用

<script setup>
    import {onMounted, ref, watch, nextTick, reactive} from 'vue';
    import {getCurrentInstance} from '@vue/runtime-core'
    import {ElMessage} from 'element-plus';
    import axios from '../http/axios';
    import html2canvas from "html2canvas";
    import 'vue-cropper/dist/index.css'
    import { VueCropper }  from "vue-cropper";

    const currentInstance = ref()

    const props = defineProps({
        caseId: Number,
        projectId: Number,
        platform: Number,
        imageUrl: String,
        stepId: Number,
    });
    //截图位置对应原图的坐标区域
    const selectRegion = ref()

    const option = ref({
        info: true, // 裁剪框的大小信息
        outputSize: 1, // 裁剪生成图片的质量
        outputType: "jpeg", // 裁剪生成图片的格式
        canScale: true, // 图片是否允许滚轮缩放
        autoCrop: false, // 是否默认生成截图框
        fixedBox: false, // 固定截图框大小 不允许改变
        fixed: false, // 是否开启截图框宽高固定比例
        fixedNumber: [1, 1], // 截图框的宽高比例
        full: false, // 是否输出原图比例的截图
        canMove: false, //是否可以移动原图
        canMoveBox: false, // 截图框能否拖动
        original: false, // 上传图片按照原始比例渲染
        centerBox: true, // 截图框是否被限制在图片里面
        infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
        mode: "1073px 589px"  //图片默认渲染方式
    })

    const uploadImg = ref('')
    const show = ref(false)

    const image = ref({
        url: props.imageUrl
    });

    const elements = ref({
        id: null,
        eleName: '',
        eleType: "image",
        eleValue: '',
        projectId: props.projectId,
        eleDeviceModel: "",
        eleAppName: "",
        eleGuiName: "",
        remark: '',//此处传递控件元素区域为了方便后期的取值,这样不需要修改数据库表格
    })

    //裁剪
    const tailoring = () => {
        nextTick(() => {
            html2canvas(document.getElementById('place'), {}).then(canvas => {
                let dataURL = canvas.toDataURL("image/png");
                uploadImg.value = dataURL
                show.value = true
            });
        })
    }

    //保存截图
    const save = () => {
        /**
         * 下面这行代码在vue2中使用
         * this.$refs.cropper.getCropData
         *
         **/
        //获取截图的base64格式数据
        currentInstance.value.refs.cropper.getCropData((data) => {
            show.value = false
            //可以在此处对文件进行上传或者其他处理
        })
        //获取截图的Blob格式数据
        currentInstance.value.refs.cropper.getCropBlob(data => {
            show.value = false
            let formData = new FormData();
            let fileName = guid();
            formData.append("file", blobToFile(data, fileName + ".png"));
            formData.append("type", 'keepFiles');
            //1.先上传截取到的小图
            axios
                .post("/folder/upload", formData, {headers: {"Content-type": "multipart/form-data"}})
                .then((resp) => {
                    if (resp['code'] === 2000) {
                        //2.图片上传完成以后,根据返回值创建一个控件元素
                        elements.value.eleName = "PlaceAssert-Ele_" + guid()
                        elements.value.eleValue = resp['data']
                        elements.value.remark = JSON.stringify(selectRegion.value)
                        const transfer = {
                            id: null,
                            projectId: '',
                            addTime: '',
                            caseId: '',
                            originalPath: image.value.url,
                            elementValue: resp['data']
                        }
                        axios.put("/controller/transfer", transfer).then(response => {
                            if (response['code'] === 2000) {
                                axios.put("/controller/elements", elements.value).then(eleResp => {
                                    if (eleResp['code'] === 2000) {
                                        ElMessage.success({
                                            message: eleResp['message'],
                                        });
                                    } else {
                                        ElMessage.error({
                                            message: eleResp['message'],
                                        });
                                    }
                                })
                            }
                        })
                    } else {
                        ElMessage.error({
                            message: resp['message']
                        })
                    }
                });
        });
    }

    /**
     * 随机生成uuid
     * */
    const guid = () => {
        return 'xxxxx-xxxx-4xxx-yxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }


    //取消
    const close = () => {
        show.value = false
    }

    //将blob转换为file
    const blobToFile = (theBlob, fileName) => {
        theBlob.lastModifiedDate = new Date();  // 文件最后的修改日期
        theBlob.name = fileName;                // 文件名
        return new File([theBlob], fileName, {type: theBlob.type, lastModified: Date.now()});
    }

    const mouseup = (e) => {
        selectRegion.value.startX = e.offsetX
        selectRegion.value.startY = e.offsetY
    }


    const mousedown = (e) => {
        selectRegion.value.endX = e.offsetX
        selectRegion.value.endY = e.offsetY

    }

    const mouseenterEnter = (e) => {
        if (uploadImg.value == "") {
            return;
        }
        //开始裁剪
        currentInstance.value.refs.cropper.startCrop();

        /**
         * 上面这行代码在 vue2 中使用方法
         * this.$refs.cropper.startCrop()
         * */
    }
    // const cropper = ref({})
    const mouseenterLeave = (e) => {

        //停止裁剪
        // currentInstance.value.ctx.$refs.cropper.stopCrop();
        currentInstance.value.refs.cropper.stopCrop();
        //获取截图框基于容器的坐标点
        // selectRegion.value = currentInstance.value.ctx.$refs.cropper.getCropAxis()
        selectRegion.value = currentInstance.value.refs.cropper.getCropAxis()
        /**
         * 上面这行代码在 vue2 中使用方法
         * this.$refs.cropper.stopCrop()
         * */
    }


    onMounted(() => {

        /**
         * 此处这样使用时因为Vue3不同于Vue2,在 Vue3的setup中我们是无法访问到this的,所以我们需要借助一个方法,
         * 那就是getCurrentInstance,该方法返回了当前的实例对象
         *
         * 注意!!!!!
         * 不要把该函数当作是optionsApi中来获取 this 使用。
         * 该方法只在 setup、生命周期函数中有效,在方法中是无效的
         * */
        currentInstance.value = getCurrentInstance()
    });
</script>
<style>
    .preview {
        width: auto;
        height: auto;
        position: absolute;
        top: 0;
        left: 0;
        border: 1px dashed #e6a23c;
        background-color: #000000;
    }

</style>

<template>
    <el-image id="place" v-model="image" style="width: 100%; height: 100%" :src="image.url"></el-image>
    <div>
        <el-button @click="tailoring" type="primary"
        ><i class="el-icon-upload el-icon--right">裁剪</i
        ></el-button>
        <!--继续写页面的其他内容 pop_alert可封装成组件使用-->
        <!--将生成的图片进行拖动截图-->
        <div class="preview" v-if="show">

            <vueCropper
                    @mouseenter="mouseenterEnter"
                    @mouseleave="mouseenterLeave"
                    ref="cropper"
                    :img="uploadImg"
                    :outputSize="option.size"
                    :outputType="option.outputType"
                    :info="true"
                    :full="option.full"
                    :canMove="option.canMove"
                    :canMoveBox="option.canMoveBox"
                    :original="option.original"
                    :autoCrop="option.autoCrop"
                    :fixed="option.fixed"
                    :fixedNumber="option.fixedNumber"
                    :centerBox="option.centerBox"
                    :infoTrue="option.infoTrue"
                    :fixedBox="option.fixedBox"
                    :mode="option.mode"
                    style="background-image:none; width: 1151px; height: 707px"
            ></vueCropper>

            <el-row>
                <el-button @click="save" type="primary"
                ><i class="el-icon-upload el-icon--right"></i
                >确认截图
                </el-button>
                <br/>
                <el-button @click="close" type="danger" icon="el-icon-delete">取消</el-button>
            </el-row>
        </div>
    </div>
</template>

目前网络上查找了很多资料,大都是支持Vue2的代码,对于最新版本的vue非常不友好,经过各种踩坑,才有了以上结果

注意⚠️,通过<script setup>语法糖的写法,其组件是默认关闭的,也就是说如果是通过$refs或者$parents来访问子组件中定义的值是拿不到的,必须通过defineExpose向外暴露你想获取的值才行。

更多详细资料,请关注官方信息GitHub - xyxiao001/vue-cropper: A simple picture clipping plugin for vue

猜你喜欢

转载自blog.csdn.net/PhilipJ0303/article/details/129560479