基于vueCropper完成多图片对比且同位置截图直接上传后台功能

整体的效果


移动一个选择框所有的选择框会同步移动

在这里插入图片描述
可以展示选择框的整体预览 下方可以单独对某一个图片进行操作
在这里插入图片描述
最后可以将截图上传到服务器 然后在页面展示出上传之后的图片

环境准备

整体环境:vue2 + vueCropper

vueCropper安装命令

npm install vue-cropper --save

代码记录

子组件代码

<template>
    <div>
        <div class="cropper">
            <vueCropper ref="cropper" :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' @realTime='realTime'>
            </vueCropper>

        </div>
        <div>
            <hr>
            <!-- 预览展示的条件 预览开关开启,截图框开启,有预览图产生 -->
            <img :src='previewImg' alt="" ref="img" style="display:block"
                v-if="isPreview && option.autoCrop && previewImg != null">

            <el-button type="primary" @click="changePrintStatus(option.autoCrop = !option.autoCrop)">截图开关</el-button>
            <el-button type="primary" @click="getPrintImgUrl" :disabled="!this.option.autoCrop">截图</el-button>
            <img :src='resImg' alt="" ref="img" style="display:block" v-if="resImg != null">
            <!-- 原始按钮调试使用可以忽略 -->
            <el-button type="primary" @click="handleClick">原始</el-button>
            <el-button type="primary" :disabled="!this.option.autoCrop" @click="changePreview(isPreview = !isPreview)">{{
                !isPreview ? '开启' : '关闭' }}预览</el-button>

        </div>
    </div>
</template>
<script>
import { VueCropper } from 'vue-cropper'
import axios from 'axios'
export default {
    props: {
        position: {
            type: Object
        },
        imgUrl: {
            type: String
        },
        Screenkey: {
            type: Number
        }
    },
    computed: {

    },
    watch: {
        'position': {
            handler() {
                this.changePos(this.position)
            },
            deep: true
        }


    },
    mounted() {
    },
    data() {
        return {
            option: {
                //../assets/6767.png
                img: this.imgUrl, // 裁剪图片地址
                outputSize: 1, // 裁剪生成图片质量
                outputType: 'jpeg', // 裁剪生成图片格式
                canScale: false, // 图片是否允许滚轮播放
                autoCrop: false, // 是否默认生成截图框 false
                info: true, // 是否展示截图框信息
                autoCropWidth: 200, // 生成截图框的宽度
                autoCropHeight: 100, // 生成截图框的高度

                canMoveBox: true, // 截图框是否可以拖动
                fixedBox: false, // 固定截图框的大小
                canMove: false, // 上传图片是否可拖动
                centerBox: true, // 截图框限制在图片里面
            },
            resImg: null, //截图后图片
            previewImg: null, // 预览后的图片
            isPreview: false,//是否预览截图
            formData: new FormData(),//保存要上传的MultipartFile信息,
        }
    },
    components: {
        VueCropper
    },
    methods: {
        //开启预览
        changePreview(status) {
            this.isPreview = status
        },

        //开始截图
        changePrintStatus(status) {
            // this.option.autoCrop = false
            this.option.autoCrop = status
            this.$refs.cropper.refresh()

        },
        realTime() {
            // const that = this
            const cropper = this.$refs.cropper
            let toParent = {
                x: cropper.cropOffsertX,
                y: cropper.cropOffsertY,
                width: cropper.cropW,
                height: cropper.cropH
            }
            //将数据返回到父组件统一管理
            this.$emit('changePosition', toParent)
            //执行加载
            //数据保存
            cropper.getCropBlob(data => {
                this.tempBlod = data
                // 这里data数据为Blob类型,blobToDataURI方法转换成base64  

                //base64可以在页面直接展示的 我这不需要再展示就不转化了
                const that = this
                this.blobToDataURI(data, function (res) {
                    that.previewImg = res
                })

            })
        },
        //点击获取图片路径
        handleClick() {
            //获取截图后的数据
            this.$refs.cropper.getCropData(data => {
                this.resImg = data
            })
        },
        blobToDataURI(blob, callback) {
            var reader = new FileReader();
            reader.readAsDataURL(blob);
            reader.onload = function (e) {
                callback(e.target.result);
            }
        },
        //渲染数据
        changePos(obj) {
            this.$refs.cropper.cropOffsertX = obj.x
            this.$refs.cropper.cropOffsertY = obj.y
            this.$refs.cropper.cropW = obj.width
            this.$refs.cropper.cropH = obj.height
        },
        //发送数据到服务端
        async sendDataToServe(data) {

            //这个上传的地址换为自己的地址
            const res = await axios.post('http://localhost:8081/upload',
                // {
                //     file: this.formData, //参数
                // },
                data,
                {
                    //请求头设置
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                }
            )
            if (res.data.code == 200) {
                //向父组件追加结果
                if (this.Screenkey != null) {
                    let obj = {
                        [`printScreen_${this.Screenkey}`]: res.data.data
                    }
                    this.$emit('addImgResultArr', obj);
                }
                this.$emit('addImgResult', res.data.data);
            }
        },
        //封装数据 发送数据
        getPrintImgUrl() {
            //数据保存
            const formData = new FormData()
            //因为服务器是接收MultipartFile类型 所以需要转为MultipartFile类型
            // 将Blob数据添加到FormData中
            formData.append('file', this.tempBlod, `image_${new Date().getTime()}.jpg`);// 第三个参数是文件名
            this.sendDataToServe(formData)
        }
    }
}
</script>
<style scoped>
.cropper {
    width: 500px;
    height: 500px;
    border: 1px solid orange;
    margin: 5px;
    display: inline-block;
    padding: 5px;
}

.cropper-container {
    background-image: none !important;
}
</style>

父组件代码

<template>
    <div>
        <!-- :ref="`child${index}`" -->
        <el-button @click="changePrintStatus" type="primary">{{ printStatus ? '开始' : '结束' }}一键截图</el-button>
        <el-button @click="getResult" type="primary" :disabled='printStatus'>一键截图</el-button>
        <el-button @click="changePreview" type="primary" :disabled='printStatus'>{{ previewStatus ?
            '开启' : '关闭' }}截图预览</el-button>
        <div class="parentBox">
            <!-- :Screenkey 可以将结果封装成一个以序号为key的数组 同时结果也会保存在 imgResult  
             也就是Screenkey存在就会生成一个对象类型的数组保存在 imgResultArr<保存obj类型>  不存在就是只会将图片地址保存在 imgResult<保存字符串类型>-->
            <PrintScreen v-for="(item, index) in imgList" :key="index" :Screenkey="index" @changePosition="changePosition"
                :position="position" ref="child" @addImgResult="addImgResult" :imgUrl="item"
                @addImgResultArr="addImgResultArr">
            </PrintScreen>
        </div>
        <div>
            <p>展示结果</p>

            <div v-if="imgResult.length > 0">
                <img :src='item' alt="" v-for="(item, index) in imgResult" :key="index">
            </div>

        </div>




    </div>
</template>

<script>
import PrintScreen from '@/components/PrintScreen.vue'
export default {

    data() {
        return {

            isDisabled: true,//是否禁用一键截图按钮
            printStatus: true,//父组件强制下发是否按时截图框
            previewStatus: true,//父组件强制下发预览状态
            position: {
                x: 0,
                y: 0,
                width: 100,
                height: 100
            },
            imgList: [
                require('../assets/2123.jpg'),
                require('../assets/2123.jpg')
            ],
            imgResult: [],//临时结果保存-字符串
            imgResultAll: [],//结果保存-字符串
            imgResultArr: [],//临时结果保存-对象
            imgResultArrAll: []//结果保存-对象
        }
    },
    components: {
        PrintScreen
    },
    methods: {
        //全部开启预览
        changePreview() {
            const childRefs = this.$refs.child;
            childRefs.forEach(async (item) => {
                item.changePreview(this.previewStatus)
            });
            //修改状态
            this.previewStatus = !this.previewStatus
        },
        //开始截图
        changePrintStatus() {
            const childRefs = this.$refs.child;
            childRefs.forEach(async (item) => {
                item.changePrintStatus(this.printStatus)
            });
            //修改状态
            this.printStatus = !this.printStatus

        },
        //一键截图
        getResult() {
            const childRefs = this.$refs.child;
            //调用子组件的截图能力
            childRefs.forEach(async (item) => {
                item.getPrintImgUrl();
            });

            // console.log(this.imgResult);
            console.log(this.imgResultArr);
            console.log(this.imgResultArrAll);
            //关闭截图框
            this.changePrintStatus();
            //将数据置空
            this.imgResult = []
            this.imgResultArr = []

        },
        changePosition(event) {
            this.position = event
        },
        //增加结果-字符串
        addImgResult(event) {
            this.imgResult.push(event)
            this.imgResultAll.push(event)
        },

        //增加结果-对象
        addImgResultArr(event) {
            this.imgResultArr.push(event)
            this.imgResultArrAll.push(event)
        }
    }
}
</script>

<style>
.parentBox {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 5px 100px;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值