整体的效果
移动一个选择框所有的选择框会同步移动
可以展示选择框的整体预览 下方可以单独对某一个图片进行操作
最后可以将截图上传到服务器 然后在页面展示出上传之后的图片
环境准备
整体环境: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>