基于vue2的图片头像剪裁工具vue-cropper

175 阅读2分钟
<image-cropper ref="iscropper" :imageFile='imageFile' @uploadSuccess='uploadSuccess'>
</image-cropper>
import imageCropper from '../component/imageCropper.vue'
export default { 
    data(){
        imageFile: ""
    },
    methods: {
        uploadSuccess(file) {
          this.imageFile = file
        },
    }
}
<template>
<div>
  <div class="user-info-head" @click="editCropper()">
    <img v-if="imageFile" :src="imageFile" title="点击上传头像" class="img-circle img-lg" />
    <div v-else>+</div>
    <img
      v-if="imageFile"
      class="el-icon-error"
      src="../images/close-btn.png"
      style="
        position: absolute;
        left: 112px;
        top: -6px;
        cursor: pointer;
        color: #b4b8bc;
        width: 16px;
        height: 16px;
      "
      @click.stop="deleteImage">

  </div>
  <el-dialog :title="title" :close-on-click-modal="false" :visible.sync="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog()">
    <el-row>
      <el-col :xs="24" :md="12" :style="{height: '350px'}">
        <vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop" :autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox" @realTime="realTime" v-if="visible" :key="visible" />
      </el-col>
      <el-col :xs="24" :md="12" :style="{height: '350px'}">
        <div class="avatar-upload-preview">
          <img :src="previews.url" :style="previews.img" />
        </div>
      </el-col>
    </el-row>
    <br />
    <el-row>
      <el-col :lg="2" :md="2">
        <el-upload action="#" :headers="uploadHeaders" :accept="'.jpg,.jpeg,.png'" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
          <el-button size="small">
            选择
            <i class="el-icon-upload el-icon--right"></i>
          </el-button>
        </el-upload>
      </el-col>
      <el-col :lg="{span: 1, offset: 2}" :md="2">
        <el-button icon="el-icon-plus" size="small" @click="changeScale(1)"></el-button>
      </el-col>
      <el-col :lg="{span: 1, offset: 1}" :md="2">
        <el-button icon="el-icon-minus" size="small" @click="changeScale(-1)"></el-button>
      </el-col>
      <el-col :lg="{span: 1, offset: 1}" :md="2">
        <el-button icon="el-icon-refresh-left" size="small" @click="rotateLeft()"></el-button>
      </el-col>
      <el-col :lg="{span: 1, offset: 1}" :md="2">
        <el-button icon="el-icon-refresh-right" size="small" @click="rotateRight()"></el-button>
      </el-col>
      <el-col :lg="{span: 2, offset: 6}" :md="2">
        <el-button type="primary" size="small" @click="uploadImg()">提 交</el-button>
      </el-col>
    </el-row>
  </el-dialog>
</div>
</template>

<script>
import axios from 'axios'
import { getToken } from '@/base/cookie'
import {
  VueCropper
} from 'vue-cropper'
export default {
  components: {
    VueCropper
  },
  props: {
    imageFile: {
      type: String,
      default: ''
    }
  },
  watch: {
    imageFile: {
      deep: true,
      immediate: true,
      handler(newValue, oldValue) {
        this.options.img = newValue
      }
    }
  },

  data() {
    return {
      uploadHeaders: {
        'X-Token': getToken()
      },
      // 是否显示弹出层
      open: false,
      // 是否显示cropper
      visible: false,
      // 弹出层标题
      title: '修改头像',
      options: {
        img: '', // 裁剪图片的地址
        info: true, // 裁剪框的大小信息
        outputSize: 1, // 裁剪生成图片的质量
        outputType: 'jpeg', // 裁剪生成图片的格式
        canScale: false, // 图片是否允许滚轮缩放
        autoCrop: true, // 是否默认生成截图框
        autoCropWidth: 300, // 默认生成截图框宽度
        autoCropHeight: 300, // 默认生成截图框高度
        fixedBox: true, // 固定截图框大小 不允许改变
        fixed: true, // 是否开启截图框宽高固定比例
        fixedNumber: [1, 1], // 截图框的宽高比例
        full: true, // 是否输出原图比例的截图
        canMoveBox: false, // 截图框能否拖动
        original: false, // 上传图片按照原始比例渲染
        centerBox: true, // 截图框是否被限制在图片里面
        infoTrue: true // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
      },
      previews: {},
      formDataData: {}
    }
  },
  mounted() {
    console.log(this.imageFile)
  },
  methods: {
    // 编辑头像
    editCropper() {
      console.log(this.imageFile, 'console.log(this.imageFile)')
      this.options.img = this.imageFile
      this.open = true
    },
    // 打开弹出层结束时的回调
    modalOpened() {
      this.visible = true
    },
    // 覆盖默认的上传行为
    requestUpload() {},
    // 向左旋转
    rotateLeft() {
      this.$refs.cropper.rotateLeft()
    },
    // 向右旋转
    rotateRight() {
      this.$refs.cropper.rotateRight()
    },
    // 图片缩放
    changeScale(num) {
      num = num || 1
      this.$refs.cropper.changeScale(num)
    },
    // 上传预处理
    beforeUpload(file) {
      const types = ['image/jpeg', 'image/jpg', 'image/png']
      const isJPG = types.includes(file.type)
      const imgType = file.type.split('/')[1]
      console.log('imgType', imgType)
      const isLt2M = file.size / 1024 / 1024 < 20
      if (!isJPG) {
        this.$message.error('上传图片只能是 JPG 或 JPEG 或 png格式!')
        return
      }
      if (!isLt2M) {
        this.$message.error('上传图片大小不能超过 20MB!')
        return
      }
      const reader = new FileReader()
      reader.readAsDataURL(file)
      reader.onload = () => {
        this.options.img = reader.result
      }
      //
      this.formDataData = {
        file: file,
        name: file.name
      }
    },
    // 上传图片
    uploadImg() {
      this.$refs.cropper.getCropBlob((data) => {
        const cloneFile = new File([data], this.formDataData.name)
        const formData = new FormData()
        formData.append('file', cloneFile)
        const config = { // 设置请求头
          headers: {
            'X-Token': getToken()
          }
        }
        axios.post(`${process.env.VUE_APP_OSS_API}/aliyunObs/uploadFile`, formData, config).then(res => {
          // 请求成功 不同的上传图片按钮调用不同的成功回调方法
          this.$emit('uploadSuccess', res.data.data.filePath)
          this.$set(this.options, 'img', res.data.data.filePath)
          this.open = false
        }).catch(e => {
          this.$message.error(e.data.msg || '上传失败')
        })
      })
    },
    // 清除数据
    deleteImage() {
      this.options.img = ''
      this.$emit('uploadSuccess', '')
    },
    // 实时预览
    realTime(data) {
      this.previews = data
    },
    // 关闭窗口
    closeDialog() {
      this.options.img = ''
      this.visible = false
    }
  }
}
</script>

<style lang="scss" scoped>
.user-info-head {
  position: relative;
  display: inline-block;
  height: 120px;
  width: 120px;
  text-align: center;
  font-size: 24px;
  line-height: 120px;
  background-color: rgb(242, 243, 245);
  color: #86909c;
  div{
    font-size: 24px
  }
}

// .user-info-head:hover:after {
//   content: '+';
//   position: absolute;
//   left: 0;
//   right: 0;
//   top: 0;
//   bottom: 0;
//   color: #eee;
//   background: rgba(0, 0, 0, 0.1);
//   font-size: 24px;
//   font-style: normal;
//   -webkit-font-smoothing: antialiased;
//   -moz-osx-font-smoothing: grayscale;
//   cursor: pointer;
//   line-height: 110px;
//   border-radius: 8px;
//   // border-radius: 50%;
//   text-align: center
// }

.img-circle {
  border-radius: 8px;
  // border-radius: 50%;
}

.img-lg {
  width: 120px;
  height: 120px;
}

.avatar-upload-preview {
  position: absolute;
  top: 50%;
  transform: translate(50%, -50%);
  width: 200px;
  height: 200px;
  border-radius: 8px;
  box-shadow: 0 0 4px #ccc;
  overflow: hidden;
}
</style>