Recently, I modified the bug, and proposed an optimization in the test: upload the picture, then translate the picture, zoom in and out to adjust the avatar, so record the whole development process of thinking.

Overall idea:

Upload the picture and get the address -> Send it to the adjustment box (zoom in, zoom out and pan the picture to determine the avatar) -> Live preview (get the adjusted picture) -> Confirm to send the adjusted address back

Using components:vue-cropper

Parent component (box) code:

<script lang="tsx"> import { Component, Prop, Vue, Emit, Watch} from 'vue-property-decorator' import cropper from './cropper. Vue 'import { EMessage,EavatarType,EavatarCutParam } from '@/views/userCenter/model' import { stripscript } from '@/utils/util' @Component({components:{cropper}}) export default class AvatarCut extends Vue {/** * personal information form * @param Object */ AvatarFile :any = null imgUrl = "// Image cropping value, Previews :any = {} beforeAvatarUpload(file:any){let _this = this; const isImage = file.type == EavatarType.PNG || file.type == EavatarType.JPG || file.type == EavatarType.JPEG; const isLt500KB = file.size < EavatarCutParam.FILESIZE; if (! isImage) { this.$message.error(this.$t('userCenter.isavatarType')); return } if (! isLt500KB) { this.$message.error(this.$t('userCenter.isavatarSize')); return } var reader = new FileReader(); // base64 reader.onload = function(e:any) { ImgUrl = e.target.result} reader.readasdataurl (file); let fileName = stripscript(file.name) if(fileName ! = file.name){ var renameFile = new File([file],fileName,{type:file.type}) console.log(file) file = renameFile console.log(file) } this.avatarFile = file; return false; } @ Emit () cutCanvas (base: any) {return {avatarPerview: base, avatarFile: enclosing avatarFile}} / / real-time monitoring cutting result, RealTime (data:any) {console.log(' KKKKKKKKK ',data) this.previews = data.data; this.cutCanvas(data.base) } render() { return ( <div class="my-message-popup-content"> <el-upload class="upload-demo" action="#" before-upload={this.beforeAvatarUpload} accept="image/jpeg,image/png,image/jpg" on-preview="handlePreview" list-type="picture" show-file-list={false}> <el-button type="primary">{this.$t('userCenter.uploadAvatars')}</el-button> <div slot="tip" class="el-upload__tip">{this.$t('userCenter.uploadLimit')}</div> </el-upload> <div class="content-file">  <div class="file-canvas"> <cropper props={{url:this.imgUrl}} onrealTime={this.realTime} ref='cropper'></cropper> </div>  <div class="file-preview"> <div class="preview-title">{this.$t('userCenter.perviewAvatars')}</div> <div class="preview-avatar"> <div style={this.previews.div} class="preview"> <img src={this.previews.url? this.previews.url: require('@/assets/images/head_default.svg')} style={this.previews.img}/> </div> </div> </div> </div> </div> ) } } </script> <style lang="less" scoped> .my-message-popup-content{ padding:20px 0 20px 0px; .upload-demo{ height: 36px; display: flex; .el-upload__tip{ line-height: 36px; margin-top: 0; margin-left: 14px; color: #999999; } /deep/ .el-button{ width: 104px; height: 38px; background: #2255B9; border-radius: 0; } } .content-file{ width: 100%; height: 208px; margin-top: 20px; margin-bottom: 40px; display: flex; .file-canvas{ width: 322px; height: 100%; position: relative; border: 1px solid #F1F1F1; .file-shade{ position: absolute; width: 100%; height: 100%; top: 0; left: 0; z-index: 10; .file-shade-top,.file-shade-bottom{ width:100%;; background: #000; Opacity: 0.6; } .file-shade-middle{ display: flex; .file-shade-left,.file-shade-right{ height: 160px; background: #000; Opacity: 0.6; } .file-cut{ width: 160px; height: 160px; border: 1px dashed #1890FF; cursor: move; } } } } .file-preview{ width: 168px; height: 100%; margin-left: 20px; border: 1px solid #F1F1F1; .preview-title{ margin-top: 36px; color: #333333; line-height: 16px; width: 100%; text-align: center; //font-weight: 600; font-size: 16px; } .preview-avatar{ width: 100%; height: 132px; margin-top:24px; text-align: center; } } } /deep/ .preview{ width: 90px; height: 90px; margin: 0 auto; overflow: hidden; border-radius: 50%; img{ width: 90px; height: 90px; z-index: -1; } } /deep/ .vue-cropper{ background: #FFFFFF; } } </style>Copy the code

Subcomponent (clipping function) code

<template> <div> <vueCropper style="width:320px; height:206px" ref="cropper" :img='url' :autoCrop="options.autoCrop" :fixedBox="options.fixedBox" :canMoveBox="options.canMoveBox" :autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :centerBox="options.centerBox" @realTime="realTime" ></vueCropper> </div> </template> <script> import { VueCropper } from 'vue-cropper' export default { name: 'cropper', components: { VueCropper }, props:{ url:{ default:'', type:String } }, data(){ return{ options: { autoCrop: CanMoveBox: false, // Screenshots cannot be dragged centerBox: False, // autoCropWidth:100, // autoCropHeight:100, // previews:{}}}, Methods :{realTime(data){realTime(data){ $refs.cropper.getCropblob ((blob)=>{this.blobTodataUrl (blob,(base)=>{base64 this.$refs.cropper.getCropblob ((blob)=>{this.blobTodataurl (blob,(base)=>{ This.$emit('realTime',{data,base})})})}, // Convert blob to base64 blobToDataURL(blob, callback) {let a = new FileReader(); a.onload = function (e) { callback(e.target.result); } a.readAsDataURL(blob); } } } </script> <style scoped> </style>Copy the code

The effect

Reference: blog.csdn.net/qq_41107231…