代码
请忽略个别业务组件,关注裁切逻辑。
总结:利用 mousedown 确定鼠标的初始化位置,利用 mousemove 随时获取当前鼠标的位置,利用和初始化位置数据的差值,来移动容器。mouseup 重置。
<template><div class="cover-image-cropper"><div class="cropper-container"><div class="cropper-container-left"><div class="origin-image"><img-pickerref="imgPicker"customClass="cover-image-cropper-picker":imgKey="originPicKey":defaultImg="defaultImg":formDataKey="selfFormDataKey"@picked="setImgUrl"@delete-click="deleteCurrentSelectedImage"/></div><div v-show="currentGraphIndex !== ''" class="cropper-bg" :style="originImgDomStyle"></div><imgv-show="currentGraphIndex !== ''":src="articleForm.origin_image_url"class="clip-image":style="[originImgDomStyle, clipImageStyle]"alt="原始图片"/><divv-show="currentGraphIndex !== ''"class="drag-resize-box":style="dragResizeBoxStyle"@mousedown.stop.self="commonMousedownEvent($event)"@mousemove.stop.self="dragResizeBoxMousemoveEvent($event)"@mouseup.stop.self="isMousedownType = false"><iv-for="(item, index) in zoomPoints"class=" zoom-point":class="item.customClass":key="index"@mousedown.stop.self="commonMousedownEvent($event)"@mousemove.stop.self="zoomPointMousemoveEvent($event, index)"@mouseup.stop.self="isMousedownType = false"></i></div></div><div class="cropper-container-right"><div class="ratio-graph-lists"><divv-for="(item, index) in ratioLists"class="single-item":class="{active: currentGraphIndex === index,disabled: !hasOriginPic,cropped: item.croppedPosition.length,}":style="item.style":data-radio="item.text"@click="handleSelectedClick(index)">{{ item.text }}</div></div><div class="save-btn" :class="{ disabled: !hasOriginPic }" @click="saveCropperImage">保存图片</div></div></div><!-- 图片选择弹窗 --><picture-select-dialog /></div></template><script>import { ImgPicker, PictureSelectDialog } from '@/components'export default {name: 'CoverImageCropper',props: {articleForm: {type: Object || String,},},data() {return {ratioLists: [{text: '1:1',initPosition: [{ x: 0, y: 0 },{ x: 70, y: 0 },{ x: 70, y: 70 },{ x: 0, y: 70 },],croppedPosition: [],initSize: { width: 70, height: 70 },style: { width: '70px', height: '70px' },key: '1_1',extraValue: { prefix: '', suffix: '_1_1' },},{text: '3:2',initPosition: [{ x: 0, y: 0 },{ x: 105, y: 0 },{ x: 105, y: 70 },{ x: 0, y: 70 },],croppedPosition: [],initSize: { width: 105, height: 70 },style: { width: '105px', height: '70px' },key: '3_2',extraValue: { prefix: '', suffix: '_3_2' },},{text: '7:5',initPosition: [{ x: 0, y: 0 },{ x: 97, y: 0 },{ x: 97, y: 70 },{ x: 0, y: 70 },],croppedPosition: [],initSize: { width: 97, height: 70 },style: { width: '97px', height: '70px' },key: '7_5',extraValue: { prefix: '', suffix: '_7_5' },},{text: '17:10',initPosition: [{ x: 0, y: 0 },{ x: 118, y: 0 },{ x: 118, y: 70 },{ x: 0, y: 70 },],croppedPosition: [],initSize: { width: 118, height: 70 },style: { width: '118px', height: '70px' },key: '17_10',extraValue: { prefix: '', suffix: '_17_10' },},{text: '21:10',initPosition: [{ x: 0, y: 0 },{ x: 143, y: 0 },{ x: 143, y: 70 },{ x: 0, y: 70 },],croppedPosition: [],initSize: { width: 143, height: 70 },style: { width: '143px', height: '70px' },key: '21_10',extraValue: { prefix: '', suffix: '_21_10' },},],// 当前比例尺下标currentGraphIndex: '',// imgPicker keyoriginPicKey: 'coverImageCropper',// formdata keysselfFormDataKey: ['cover_img_id', 'origin_image_url'],// 原图尺寸originImgSize: {},// 原图初始化位置originImgPosition: {},// 原图 DOM 样式originImgDomStyle: {},// 裁切图片 DOM 样式clipImageStyle: {},// 拖拽、缩放容器 DOM 样式dragResizeBoxStyle: {},// 当前裁切后图片的位置信息currentPositionCoordinates: [],// 持续长按中isMousedownType: false,// 鼠标按下位置originPosition: {x: null,y: null,},zoomPoints: [{ customClass: 'top-left-point' },{ customClass: 'top-right-point' },{ customClass: 'bottom-right-point' },{ customClass: 'bottom-left-point' },],// 待提交的数据submitMsg: {},}},components: {ImgPicker,PictureSelectDialog,},computed: {defaultImg() {return {id: this.articleForm.cover_img_id,file_path: this.articleForm.origin_image_url,}},hasOriginPic() {return this.articleForm.origin_image_url !== ''},},watch: {hasOriginPic: {handler(newVal, oldVal) {if (!newVal) {this.currentGraphIndex = ''this.isMousedownType = falsethis.ratioLists.map((item) => {item.croppedPosition.length = 0})}},immediate: true,},currentGraphIndex: {handler(newVal, oldVal) {if (newVal !== '') {this.$nextTick(() => {// 已经选取裁切图逻辑const currentGraphCroppedPosition = this.ratioLists[newVal].croppedPositionif (currentGraphCroppedPosition.length) {this.updateCropperView(currentGraphCroppedPosition)this.currentPositionCoordinates = this.ratioLists[newVal].croppedPosition} else {// 未选取裁切图逻辑const originImgDom = this.$refs.imgPicker.$refs.imgconst offsetWidth = originImgDom.offsetWidthconst offsetHeight = originImgDom.offsetHeightconst offsetTop = originImgDom.offsetTopconst offsetLeft = originImgDom.offsetLeft/*** originImgSize* 原图长宽尺寸*/this.originImgSize = {width: offsetWidth,height: offsetHeight,}/*** originImgPosition* 原图初始化位置*/this.originImgPosition = {top: offsetTop,left: offsetLeft,}/*** originImgDomStyle* 原图长宽样式*/this.originImgDomStyle = {top: offsetTop + 1 + 'px',left: offsetLeft + 1 + 'px',width: offsetWidth + 'px',height: offsetHeight + 'px',}/*** currentPositionCoordinates* 当前位置坐标*/let currentInitPosition = JSON.parse(JSON.stringify(this.ratioLists[newVal].initPosition))if (offsetTop > 0 || offsetLeft > 0) {currentInitPosition.map((item) => {if (offsetTop > 0) {item.y = item.y + offsetTop}if (offsetLeft > 0) {item.x = item.x + offsetLeft}})}this.currentPositionCoordinates = currentInitPosition/*** 根据最新坐标信息更新视图*/this.updateCropperView(this.currentPositionCoordinates)}})}},},},methods: {setImgUrl(data) {const { file_id, file_path } = data[this.originPicKey]this.articleForm.cover_img_id = file_idthis.articleForm.origin_image_url = file_path},getClipImageStyle(currentPositionCoordinates) {// polygon(0px 0px, 100px 0px, 100px 100px, 0px 100px)const selfOriginImgPosition = this.originImgPositionreturn {'clip-path': `polygon(${currentPositionCoordinates.map((item, index) =>item.x -this.originImgPosition.left +'px' +' ' +(item.y - this.originImgPosition.top) +'px' +(index < currentPositionCoordinates.length - 1 ? ', ' : '')).join('')})`,}},/*** 获取拖动、缩放容器的位置样式* @param {array} currentPositionCoordinates - 当前位置信息** @return {object} 返回 style 对象*/getDragResizeBoxStyle(currentPositionCoordinates) {return {top: currentPositionCoordinates[0].y + 1 + 'px',left: currentPositionCoordinates[0].x + 1 + 'px',width: currentPositionCoordinates[1].x - currentPositionCoordinates[0].x + 'px',height: currentPositionCoordinates[3].y - currentPositionCoordinates[0].y + 'px',}},/*** 获取 mousedown 事件出发点的位置坐标* @param {object} evt - 鼠标点击事件对象*/commonMousedownEvent(evt) {this.isMousedownType = truethis.originPosition.x = evt.offsetXthis.originPosition.y = evt.offsetY},/*** 裁切图片拖拽事件* @param {object} evt - 拖拽事件对象*/dragResizeBoxMousemoveEvent(evt) {// console.log('***', evt.offsetX)if (this.isMousedownType) {const selfOriginImgSize = this.originImgSizeconst selfOriginImgPosition = this.originImgPositionconst selfCurrentPositionCoordinates = this.currentPositionCoordinatesconst x = evt.offsetXconst y = evt.offsetYconst distanceX = x - this.originPosition.xconst distanceY = y - this.originPosition.yif (distanceX + selfCurrentPositionCoordinates[0].x < selfOriginImgPosition.left ||distanceY + selfCurrentPositionCoordinates[0].y < selfOriginImgPosition.top ||distanceX + selfCurrentPositionCoordinates[1].x >selfOriginImgSize.width + selfOriginImgPosition.left ||distanceY + selfCurrentPositionCoordinates[3].y >selfOriginImgSize.height + selfOriginImgPosition.top) {return false} else {this.currentPositionCoordinates = selfCurrentPositionCoordinates.map((item) => {return {x: item.x + distanceX,y: item.y + distanceY,}})this.updateCropperView(this.currentPositionCoordinates)}}},/*** 裁切图片缩放事件* 根据 mousemove 缩放图片* @param {object} evt - 拖拽事件对象* @param {number} currentPointIndex - 拖拽的 point,0: ↖️, 1: ↗️, 2: ↘️, 3: ↙️*/zoomPointMousemoveEvent(evt, currentPointIndex) {if (this.isMousedownType) {const x = evt.offsetXconst distance = x - this.originPosition.x// 判读是否可以进行容器缩放if (this.zoomCanPass(currentPointIndex, distance)) {return false} else {this.currentPositionCoordinates = this.currentPositionCoordinates.map((item, index) => {let isEven = (currentPointIndex + 1) % 2 === 0// 当前缩放触发方向// x,y 坐标都变动if (currentPointIndex === index) {return {x: item.x + distance,y: item.y + distance,}}// 顺时针下一位if ((currentPointIndex + 1) % 4 === index) {return {x: isEven ? item.x + distance : item.x,y: isEven ? item.y : item.y + distance,}}// 对角方向if ((currentPointIndex + 2) % 4 === index) {return {x: item.x,y: item.y,}}// 逆时针上一位if ((currentPointIndex + 3) % 4 === index)return {x: isEven ? item.x : item.x + distance,y: isEven ? item.y + distance : item.y,}})this.updateCropperView(this.currentPositionCoordinates)}}},/*** 判断缩放是否可执行* @param {number} currentPointIndex - 拖拽的 point,0: ↖️, 1: ↗️, 2: ↘️, 3: ↙️* @param {number} distance - 缩放的大小值** @return {boolean} 返回是否可执行的布尔值*/zoomCanPass(currentPointIndex, distance) {const selfCurrentPositionCoordinates = this.currentPositionCoordinatesconst selfOriginImgSize = this.originImgSizeconst selfOriginImgPosition = this.originImgPositionswitch (currentPointIndex) {case 0:returndistance + selfCurrentPositionCoordinates[0].x < selfOriginImgPosition.left ||distance + selfCurrentPositionCoordinates[0].y < selfOriginImgPosition.topbreakcase 1:distance + selfCurrentPositionCoordinates[1].x >selfOriginImgSize.width + selfOriginImgPosition.left ||distance + selfCurrentPositionCoordinates[1].y < selfOriginImgPosition.topbreakcase 2:distance + selfCurrentPositionCoordinates[2].x >selfOriginImgSize.width + selfOriginImgPosition.left ||distance + selfCurrentPositionCoordinates[2].y >selfOriginImgSize.height + selfOriginImgPosition.topbreakcase 3:distance + selfCurrentPositionCoordinates[3].x < selfOriginImgPosition.left ||distance + selfCurrentPositionCoordinates[3].y >selfOriginImgSize.height + selfOriginImgPosition.topbreakdefault:return false}},/*** 根据最新坐标信息更新视图* @param {object} selfCurrentPositionCoordinates - 当前裁切后图片的位置信息*/updateCropperView(selfCurrentPositionCoordinates) {/*** clip-image* 利用 css3 属性 clip-path*/this.clipImageStyle = this.getClipImageStyle(selfCurrentPositionCoordinates)/*** dragResizeBoxStyle* 用于拖动、缩放的元素*/this.dragResizeBoxStyle = this.getDragResizeBoxStyle(selfCurrentPositionCoordinates)},/*** 点击选中当前比例* @param {number} index - 当前比例的下标*/handleSelectedClick(index) {if (this.hasOriginPic && this.currentGraphIndex !== index) {this.currentGraphIndex = indexthis.isMousedownType = false}},/*** 保存当前裁切比例下的图片位置信息*/saveCropperImage() {const currentGraph = this.ratioLists[this.currentGraphIndex]const singleCoverScale = {}// 保存裁切后的位置信息currentGraph.croppedPosition = this.currentPositionCoordinates// 表单提交所需要的比例信息singleCoverScale.x = currentGraph.croppedPosition[0].xsingleCoverScale.y = currentGraph.croppedPosition[0].ysingleCoverScale.width = currentGraph.croppedPosition[1].x - currentGraph.croppedPosition[0].xsingleCoverScale.heigt = currentGraph.croppedPosition[3].y - currentGraph.croppedPosition[0].yObject.assign(singleCoverScale, currentGraph.extraValue)console.log('保存图片', singleCoverScale)if (!this.submitMsg[currentGraph.key]) {this.submitMsg[currentGraph.key] = singleCoverScale} else {this.$delete(this.submitMsg, currentGraph.key)}},/*** 删除当前选中图回调函数* @param {array, string} formDataKey - 待删除图片表单项属性 key*/deleteCurrentSelectedImage(formDataKey) {if (Array.isArray(formDataKey)) {formDataKey.forEach((singleKey) => {this.$set(this.articleForm, singleKey, '')})} else {this.$set(this.articleForm, formDataKey, '')}},},}</script><style lang="scss" scoped>.cover-image-cropper {position: relative;width: 100%;height: auto;.cropper-container {display: flex;&-left {position: relative;.origin-image {.img-picker.cover-image-cropper-picker {width: 256px;height: 176px;line-height: 176px;.delete-icon-zIndex {z-index: 9;}}}.cropper-bg,.clip-image {position: absolute;top: 1px;left: 1px;}.cropper-bg {width: 100%;background: rgba(0, 0, 0, 0.35);}.clip-image {width: 100%;}.drag-resize-box {position: absolute;cursor: move;.zoom-point {position: absolute;width: 4px;height: 4px;background-color: #000;&.top-left-point {top: -2px;left: -2px;cursor: nwse-resize;}&.top-right-point {top: -2px;right: -2px;cursor: nesw-resize;}&.bottom-right-point {bottom: -2px;right: -2px;cursor: nwse-resize;}&.bottom-left-point {bottom: -2px;left: -2px;cursor: nesw-resize;}}}}&-right {position: relative;margin-left: 85px;&::before {content: ' ';position: absolute;top: 26px;left: -42px;width: 1px;height: 112px;background: rgba(240, 242, 245, 1);}.ratio-graph-lists {position: relative;display: flex;flex-wrap: wrap;.single-item {position: relative;margin-left: 22px;border: 1px solid rgba(216, 220, 230, 1);line-height: 70px;text-align: center;font-size: 14px;font-family: PingFangSC-Regular, PingFang SC;color: rgba(144, 147, 153, 1);background: rgba(240, 242, 245, 1);cursor: pointer;&:nth-of-type(1) {margin-left: 0;}/** 兼容小屏幕,换行处理 */@media (max-width: 1443px) {&:nth-of-type(4) {margin-left: 0;}&:nth-of-type(4),&:nth-of-type(5) {margin-top: 35px;}}&.active {border-color: rgba(25, 137, 250, 1);color: rgba(25, 137, 250, 1);background-color: rgba(240, 242, 245, 1);}&.disabled {pointer-events: none;}&.cropped {&::after {content: '已获取';position: absolute;bottom: -27px;left: 50%;margin-left: -26px;border-radius: 9px;width: 52px;height: 18px;line-height: 18px;text-align: center;font-size: 12px;font-family: PingFangSC-Regular, PingFang SC;color: rgba(4, 134, 254, 1);background: rgba(224, 240, 255, 1);}}}}.save-btn {margin-top: 70px;border-radius: 4px;width: 96px;height: 36px;line-height: 36px;text-align: center;font-size: 14px;font-family: PingFangSC-Regular, PingFang SC;color: rgba(255, 255, 255, 1);background: rgba(25, 137, 250, 1);cursor: pointer;&.disabled {color: rgba(144, 147, 153, 1);background-color: rgba(240, 242, 245, 1);pointer-events: none;}@media (max-width: 1443px) {margin-top: 35px;}}}}}</style>
vue-cropper
使用 vue-cropper 实现同样的需求
<template><div class="cropper-wrapper"><div class="cropper-content"><div class="cropper-content-left"><div v-if="!hasOriginPic" class="origin-image"><img-pickerref="imgPicker"customClass="cover-image-cropper-picker":imgKey="originPicKey":defaultImg="defaultImg":formDataKey="selfFormDataKey"@picked="setImgUrl"@delete-click="deleteCurrentSelectedImage"/></div><div v-else class="my-cropper-box"><svg-iconiconName="icon-delete-file"className="delete-icon":styleObject="deleteIconStyle"@handleClickEvent="deleteCurrentSelectedImage"/><vueCropperref="cropper":img="articleForm.cover_img":outputSize="option.size":info="true":full="option.full":canMove="option.canMove":canMoveBox="option.canMoveBox":fixedBox="option.fixedBox":original="option.original":autoCrop="option.autoCrop":autoCropWidth="option.autoCropWidth":autoCropHeight="option.autoCropHeight":centerBox="option.centerBox":high="option.high":infoTrue="option.infoTrue":maxImgSize="option.maxImgSize"@cropMoving="cropMoving":enlarge="option.enlarge":mode="option.mode":limitMinSize="option.limitMinSize":fixed="option.fixed":fixedNumber="option.fixedNumber"></vueCropper></div></div><div class="cropper-content-right"><div class="ratio-graph-lists"><divv-for="(item, index) in ratioLists"class="single-item":class="{active: currentGraphIndex === index,disabled: !hasOriginPic,cropped: item.isCropped,hasEditStyle: !item.isCropped && Object.keys(item.editStyle).length,}":style="[item.style, !item.isCropped ? item.editStyle : '']":data-radio="item.text"@click="handleSelectedClick(index)"><div class="text-layer"><span>{{ item.text }}</span></div></div></div><div class="save-btn" :class="{ disabled: !hasOriginPic }" @click="saveCropperImage">保存图片</div></div></div><!-- 图片选择弹窗 --><picture-select-dialog /></div></template><script>import { VueCropper } from 'vue-cropper'import { ImgPicker, PictureSelectDialog } from '@/components'export default {name: 'CoverImageCropperPlus',props: {articleForm: {type: Object || String,},},data() {return {ratioLists: [{text: '1:1',isCropped: false,croppedPosition: {},style: { width: '70px', height: '70px' },editStyle: {},scaleFactor: [1, 1],key: '1_1',extraValue: { prefix: '', suffix: '_1_1' },},{text: '3:2',isCropped: false,croppedPosition: {},style: { width: '105px', height: '70px' },editStyle: {},scaleFactor: [3, 2],key: '3_2',extraValue: { prefix: '', suffix: '_3_2' },},{text: '7:5',isCropped: false,croppedPosition: {},style: { width: '97px', height: '70px' },editStyle: {},scaleFactor: [7, 5],key: '7_5',extraValue: { prefix: '', suffix: '_7_5' },},{text: '17:10',isCropped: false,croppedPosition: {},style: { width: '118px', height: '70px' },editStyle: {},scaleFactor: [17, 10],key: '17_10',extraValue: { prefix: '', suffix: '_17_10' },},{text: '21:10',isCropped: false,croppedPosition: {},style: { width: '143px', height: '70px' },editStyle: {},scaleFactor: [21, 10],key: '21_10',extraValue: { prefix: '', suffix: '_21_10' },},],// 当前比例尺currentGraphIndex: '',// imgPicker keyoriginPicKey: 'CoverImageCropperPlus',// formdata keysselfFormDataKey: ['cover_img_id', 'cover_img'],// 待提交的数据submitMsg: {},deleteIconStyle: {width: '14px',height: '14px',},crap: false,option: {size: 1,full: false,outputType: 'png',canMove: false,fixedBox: false,original: false,canMoveBox: true,autoCrop: false,centerBox: true,high: false,cropData: {},enlarge: 1,mode: 'contain',maxImgSize: 2000,fixed: true,fixedNumber: [1, 1],},}},components: {VueCropper,ImgPicker,PictureSelectDialog,},computed: {defaultImg() {return {id: this.articleForm.cover_img_id,file_path: this.articleForm.cover_img,}},hasOriginPic() {return this.articleForm.cover_img !== ''},},watch: {hasOriginPic: {handler(newVal, oldVal) {if (!newVal) {this.currentGraphIndex = ''this.option.canMove = falsethis.ratioLists.map((item) => {item.isCropped = falseitem.croppedPosition = {}item.editStyle = {}})}},immediate: true,},currentGraphIndex: {handler(newVal, oldVal) {if (newVal !== '') {this.option.canMove = truethis.$nextTick(() => {const selfRatioItem = this.ratioLists[newVal]const selfIsCropped = selfRatioItem.isCroppedconst selfPositionMsg = selfRatioItem.croppedPosition// update current cropper ratiothis.option.fixedNumber = selfRatioItem.scaleFactorthis.$nextTick(() => {// auto cropthis.$refs.cropper.goAutoCrop()this.$nextTick(() => {// if cropped and has position message, update crop boxif (selfIsCropped && selfPositionMsg) {this.$refs.cropper.cropOffsertX = selfPositionMsg.xthis.$refs.cropper.cropOffsertY = selfPositionMsg.ythis.$refs.cropper.cropW = selfPositionMsg.widththis.$refs.cropper.cropH = selfPositionMsg.height}})})})}},immediate: true,},},methods: {/** 设置选中的图片*/setImgUrl(data) {const { file_id, file_path } = data[this.originPicKey]this.articleForm.cover_img_id = file_idthis.articleForm.cover_img = file_path// 裁切组件获取图片地址this.option.img = file_path},/*** 删除当前选中图回调函数*/deleteCurrentSelectedImage() {if (Array.isArray(this.selfFormDataKey)) {this.selfFormDataKey.forEach((singleKey) => {this.$set(this.articleForm, singleKey, '')})} else {this.$set(this.articleForm, this.selfFormDataKey, '')}},/** 裁切图回显 */setPreviewStyle() {this.ratioLists.map((item, index) => {const selfCoverImg = JSON.parse(JSON.stringify(this.articleForm.cover_img))const coverImgSplitArr = selfCoverImg.split('.')const covreImgSuffix = coverImgSplitArr[coverImgSplitArr.length - 1]const coverImgPrefix = coverImgSplitArr.splice(0, coverImgSplitArr.length - 1).join('.')const newICoverImg =coverImgPrefix +item.extraValue.suffix +'.' +covreImgSuffix +'?timestamp=' +new Date().getTime()item.editStyle = {background: `url(${newICoverImg}) no-repeat 0 0`,backgroundSize: 'contain',}})},/*** 点击选中当前比例* @param {number} index - 当前比例的下标*/handleSelectedClick(index) {if (this.hasOriginPic && this.currentGraphIndex !== index) {this.currentGraphIndex = index}},/*** 保存当前裁切比例下的图片位置信息*/saveCropperImage() {const imgScale = this.$refs.cropper.scaleconst { x1: cropX1, x2: cropX2, y1: cropY1, y2: cropY2 } = this.$refs.cropper.getCropAxis()const { x1: imgX1, x2: imgX2, y1: imgY1, y2: imgY2 } = this.$refs.cropper.getImgAxis()// console.log('cropBox axis: ', cropX1, cropX2, cropY1, cropY1)// console.log('image axis: ', imgX1, imgX2, imgY1, imgY2)const currentGraph = this.ratioLists[this.currentGraphIndex]currentGraph.isCropped = true// 保存裁切后的位置信息currentGraph.croppedPosition = Object.assign({},{x: cropX1,y: cropY1,width: cropX2 - cropX1,height: cropY2 - cropY1,})// 表单提交所需要的比例信息const singleCoverScale = {}singleCoverScale.x = Math.round((cropX1 - imgX1) / imgScale)singleCoverScale.y = Math.round((cropY1 - imgY1) / imgScale)singleCoverScale.width = Math.round((cropX2 - cropX1) / imgScale)singleCoverScale.height = Math.round((cropY2 - cropY1) / imgScale)Object.assign(singleCoverScale, currentGraph.extraValue)if (this.submitMsg[currentGraph.key]) {this.$delete(this.submitMsg, currentGraph.key)}this.$set(this.submitMsg, currentGraph.key, singleCoverScale)console.log('submitMsg', this.submitMsg)},},}</script><style lang="scss" scoped>.test-button {display: flex;flex-wrap: wrap;}.btn {display: inline-block;line-height: 1;white-space: nowrap;cursor: pointer;background: #fff;border: 1px solid #c0ccda;color: #1f2d3d;text-align: center;box-sizing: border-box;outline: none;margin: 20px 10px 0px 0px;padding: 9px 15px;font-size: 14px;border-radius: 4px;color: #fff;background-color: #50bfff;border-color: #50bfff;transition: all 0.2s ease;text-decoration: none;user-select: none;}.cropper-content {display: flex;&-left {position: relative;width: 256px;height: 176px;.origin-image {position: absolute;top: 0;left: 0;z-index: 9;.img-picker.cover-image-cropper-picker {width: 256px;height: 176px;line-height: 176px;.delete-icon-zIndex {z-index: 9;}}}.my-cropper-box {position: relative;width: 256px;height: 176px;border: 1px dashed #dcdfe6;&:hover {border-color: #409eff;.delete-icon {visibility: visible;}}.delete-icon {visibility: hidden;z-index: 99;position: absolute;top: -7px;right: -7px;cursor: pointer;}}}&-right {position: relative;margin-left: 85px;&::before {content: ' ';position: absolute;top: 26px;left: -42px;width: 1px;height: 112px;background: rgba(240, 242, 245, 1);}.ratio-graph-lists {position: relative;display: flex;flex-wrap: wrap;/** 兼容小屏幕,换行处理 */@media (max-width: 1443px) {width: 316px;.single-item {&:nth-of-type(4) {margin-left: 0;}&:nth-of-type(4),&:nth-of-type(5) {margin-top: 35px;}}}.single-item {position: relative;margin-left: 22px;border: 1px solid rgba(216, 220, 230, 1);line-height: 70px;text-align: center;font-size: 14px;font-family: PingFangSC-Regular, PingFang SC;color: rgba(144, 147, 153, 1);background: rgba(240, 242, 245, 1);cursor: pointer;&:nth-of-type(1) {margin-left: 0;}&:hover,&.active {border-color: rgba(25, 137, 250, 1);color: rgba(25, 137, 250, 1);background-color: rgba(240, 242, 245, 1);}&.disabled {pointer-events: none;}&.cropped {&::after {content: '已获取';position: absolute;bottom: -27px;left: 50%;margin-left: -26px;border-radius: 9px;width: 52px;height: 18px;line-height: 18px;text-align: center;font-size: 12px;font-family: PingFangSC-Regular, PingFang SC;color: rgba(4, 134, 254, 1);background: rgba(224, 240, 255, 1);}}&.hasEditStyle {.text-layer {color: #fff;background-color: rgba(0, 0, 0, 0.15);}}}}.save-btn {margin-top: 70px;border-radius: 4px;width: 96px;height: 36px;line-height: 36px;text-align: center;font-size: 14px;font-family: PingFangSC-Regular, PingFang SC;color: rgba(255, 255, 255, 1);background: rgba(25, 137, 250, 1);cursor: pointer;&:hover {border-color: #369efe;background-color: #369efe;}&.disabled {color: rgba(144, 147, 153, 1);background-color: rgba(240, 242, 245, 1);pointer-events: none;}@media (max-width: 1443px) {margin-top: 35px;}}}}</style>

