美文网首页
vue 上传头像可裁剪

vue 上传头像可裁剪

作者: Hsugar | 来源:发表于2020-09-09 12:04 被阅读0次

    install cropperjs 裁剪图片,利用getCroppedCanvas方法。

    npm install cropperjs --save 
    或者
    yarn add cropperjs
    

    上传图片传参一般是form-data格式,form标签需要添加enctype="multipart/form-data"

    <form enctype="multipart/form-data" class="handle_add" name="fileinfo" @click="handleAdd">
        <input type="file" id="change" ref="inputFile" :accept="accept" @change="change">
        <label for="change"></label>
    </form>
    

    需要注意的是:多次上传同一个图片,会导致两个问题:
    1、点击上传选择完图片触发不了裁剪界面;2、上传完成请求接口成功后图片未更新

    解决1: change事件监听value而触发,而value在上传文件的时候保存的是文件的内容。需要在上传成功的回调里面,将当前input的value值置空即可。event.target.value=”;

    解决2: 在new File的第二参数加上当前时间戳就好啦

    贴上完整代码

    <template>
      <div class="Upload">
        <!-- 遮罩层 -->
        <div class="container" v-show="panel">
          <div>
            <img id="image" :src="url"  alt="Picture">
          </div>
          <button type="button" class="submit" @click="crop">确定</button>
          <button type="button" class="cancle" @click="panel=false">取消</button>
        </div>
        <div>
          <div class="show" @click="handleAdd">
            <div v-if="headerImage" class="picture" :style="'backgroundImage:url('+headerImage+')'"></div>
            <img v-else :src="imgUrl" alt="">
          </div>
          <form enctype="multipart/form-data" class="handle_add" name="fileinfo" @click="handleAdd">
            <input type="file" id="change" ref="inputFile" :accept="accept" @change="change">
            <label for="change"></label>
          </form>
        </div>
      </div>
    </template>
    
    <script>
    import Cropper from 'cropperjs'
    export default {
      props: {
        imgUrl: {
          default: ''
        },
        accept: {
          type: String,
          default: 'image/jpeg,image/jpg,image/png,,image/gif'
        },
        fileSize: {
          type: Number,
          default: 1
        }
      },
      data () {
        return {
          headerImage: '',
          picValue: '',
          cropper: '',
          croppable: false,
          panel: false,
          url: ''
        }
      },
      mounted () {
        // 初始化这个裁剪框
        const self = this
        const image = document.getElementById('image')
        this.cropper = new Cropper(image, {
          aspectRatio: 1,
          viewMode: 1,
          background: false,
          zoomable: false,
          ready: function () {
            self.croppable = true
          }
        })
      },
      methods: {
        limitUpload (file) {
          if (file.size > this.fileSize * 1024 * 1024) {
            this.$message({
              type: 'error',
              message: `图片大小不允许超过${this.fileSize}M`
            })
            return false
          }
        },
        getObjectURL (file) {
          // this.limitUpload(file)
          let url = null
          if (window.createObjectURL !== undefined) { // basic
            url = window.createObjectURL(file)
          } else if (window.URL !== undefined) { // mozilla(firefox)
            url = window.URL.createObjectURL(file)
          } else if (window.webkitURL !== undefined) { // webkit or chrome
            url = window.webkitURL.createObjectURL(file)
          }
          return url
        },
        change (e) {
          const files = e.target.files || e.dataTransfer.files
          if (!files.length) return
          this.panel = true
          this.picValue = files[0]
          this.url = this.getObjectURL(this.picValue)
          if (this.cropper) {
            this.cropper.replace(this.url)
          }
          this.panel = true
          event.target.value = ''
        },
        crop () {
          this.panel = false
          if (!this.croppable) {
            return
          }
          const croppedCanvas = this.cropper.getCroppedCanvas({
          // 添加参数提升清晰度
            width: 800,
            height: 800,
            imageSmoothingQuality: 'high'
          })
          const roundedCanvas = this.getRoundedCanvas(croppedCanvas)
          // 如果不需要请求接口可直接将url更新到视图
          // this.headerImage = roundedCanvas.toDataURL()
          // 接口接收file内容的blob对象
          roundedCanvas.toBlob((data) => {
            const file = new File([data], `${new Date().getTime()}.png`, {
              type: 'image/png'
            })
            this.postImg(file)
          })
        },
        getRoundedCanvas (sourceCanvas) {
          const canvas = document.createElement('canvas')
          const context = canvas.getContext('2d')
          const width = sourceCanvas.width
          const height = sourceCanvas.height
    
          canvas.width = Math.min(300, width)
          canvas.height = Math.min(300, height)
    
          context.imageSmoothingEnabled = true
          // context.drawImage(sourceCanvas, 0, 0, width, height)  
          // 原图过大导致上传读取慢,截取300*300的尺寸
          context.drawImage(sourceCanvas, 0, 0, width, height, 0, 0, canvas.width, canvas.height)
          context.globalCompositeOperation = 'destination-in'
          context.beginPath()
          context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true)
          context.fill()
          return canvas
        },
        // 触发上传
        handleAdd () {
          this.$refs.inputFile.dispatchEvent(new MouseEvent('click'))
        },
        postImg (file) {
          const param = new FormData()
          param.append('file', file)
          // 图片的上传
          xxx(param).then((res) => {
            const { avatar } = res.data.data
            const info = {
              ...this.$store.state.userInfo,
              avatar,
            }
            // 更新store的用户信息 
            this.$store.commit('SET_USERINFO', info)
          }).catch(e => {
            this.$message.error('上传失败')
          })
        }
      }
    }
    </script>
    
    <style lang="less" >
    .Upload{
      position: relative;
      .submit,.cancle {
        position: absolute;
        right: 10px;
        top: 10px;
        width: 80px;
        height: 40px;
        border:none;
        border-radius: 5px;
        background:white;
        cursor: pointer;
      }
      .cancle{
        right: 100px;
      }
      .handle_add{
        display: none;
      }
      .show {
        width: 100px;
        height: 100px;
        overflow: hidden;
        position: relative;
        border-radius: 50%;
        cursor: pointer;
        img{
          width: 100px;
          height: 100px;
          border-radius: 50%;
        }
      }
      .picture {
        width: 100%;
        height: 100%;
        overflow: hidden;
        background-position: center center;
        background-repeat: no-repeat;
        background-size: cover;
      }
      .container {
        z-index: 99;
        position: fixed;
        width: 50%;
        height: 50%;
        padding-top: 60px;
        left: 50%;
        top: 40%;
        transform: translate(-50%,-50%);
        background:rgba(0,0,0,1);
      }
      #image {
        max-width: 100%;
      }
    }
      .cropper-view-box,.cropper-face {
        border-radius: 50%;
      }
      /*!
       * Cropper.js v1.0.0-rc
       * https://github.com/fengyuanchen/cropperjs
       *
       * Copyright (c) 2017 Fengyuan Chen
       * Released under the MIT license
       *
       * Date: 2017-03-25T12:02:21.062Z
       */
    
      .cropper-container {
        font-size: 0;
        line-height: 0;
    
        position: relative;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        direction: ltr;
        -ms-touch-action: none;
        touch-action: none
      }
    
      .cropper-container img {
        /* Avoid margin top issue (Occur only when margin-top <= -height) */
        display: block;
        min-width: 0 !important;
        max-width: none !important;
        min-height: 0 !important;
        max-height: none !important;
        width: 100%;
        height: 100%;
        image-orientation: 0deg
      }
    
      .cropper-wrap-box,
      .cropper-canvas,
      .cropper-drag-box,
      .cropper-crop-box,
      .cropper-modal {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
      }
      .cropper-wrap-box {
        overflow: hidden;
      }
    
      .cropper-drag-box {
        opacity: 0;
        background: #fff;
      }
    
      .cropper-modal {
        opacity: .5;
        background: #000;
      }
    
      .cropper-view-box {
        display: block;
        overflow: hidden;
    
        width: 100%;
        height: 100%;
    
        outline: 1px solid #39f;
        outline-color: rgba(51, 153, 255, 0.75);
      }
    
      .cropper-dashed {
        position: absolute;
    
        display: block;
    
        opacity: .5;
        border: 0 dashed #eee
      }
    
      .cropper-dashed.dashed-h {
        top: 33.33333%;
        left: 0;
        width: 100%;
        height: 33.33333%;
        border-top-width: 1px;
        border-bottom-width: 1px
      }
    
      .cropper-dashed.dashed-v {
        top: 0;
        left: 33.33333%;
        width: 33.33333%;
        height: 100%;
        border-right-width: 1px;
        border-left-width: 1px
      }
    
      .cropper-center {
        position: absolute;
        top: 50%;
        left: 50%;
        display: block;
        width: 0;
        height: 0;
        opacity: .75
      }
    
      .cropper-center:before,
      .cropper-center:after {
        position: absolute;
        display: block;
        content: ' ';
        /* #eee */
      }
    
      .cropper-center:before {
        top: 0;
        left: -3px;
        width: 7px;
        height: 1px
      }
    
      .cropper-center:after {
        top: -3px;
        left: 0;
        width: 1px;
        height: 7px
      }
    
      .cropper-face,
      .cropper-line,
      .cropper-point {
        position: absolute;
        display: block;
        width: 100%;
        height: 100%;
        opacity: .1;
      }
    
      .cropper-face {
        top: 0;
        left: 0;
    
        /* #fff; */
      }
      .cropper-line.line-e {
        top: 0;
        right: -3px;
        width: 5px;
        cursor: e-resize
      }
    
      .cropper-line.line-n {
        top: -3px;
        left: 0;
        height: 5px;
        cursor: n-resize
      }
    
      .cropper-line.line-w {
        top: 0;
        left: -3px;
        width: 5px;
        cursor: w-resize
      }
    
      .cropper-line.line-s {
        bottom: -3px;
        left: 0;
        height: 5px;
        cursor: s-resize
      }
    
      .cropper-point {
        width: 5px;
        height: 5px;
    
        opacity: .75;
        /* #39f */
      }
    
      .cropper-point.point-e {
        top: 50%;
        right: -3px;
        margin-top: -3px;
        cursor: e-resize
      }
    
      .cropper-point.point-n {
        top: -3px;
        left: 50%;
        margin-left: -3px;
        cursor: n-resize
      }
    
      .cropper-point.point-w {
        top: 50%;
        left: -3px;
        margin-top: -3px;
        cursor: w-resize
      }
    
      .cropper-point.point-s {
        bottom: -3px;
        left: 50%;
        margin-left: -3px;
        cursor: s-resize
      }
    
      .cropper-point.point-ne {
        top: -3px;
        right: -3px;
        cursor: ne-resize
      }
    
      .cropper-point.point-nw {
        top: -3px;
        left: -3px;
        cursor: nw-resize
      }
    
      .cropper-point.point-sw {
        bottom: -3px;
        left: -3px;
        cursor: sw-resize
      }
    
      .cropper-point.point-se {
        right: -3px;
        bottom: -3px;
        width: 20px;
        height: 20px;
        cursor: se-resize;
        opacity: 1
      }
    
      @media (min-width: 768px) {
    
        .cropper-point.point-se {
          width: 15px;
          height: 15px
        }
      }
    
      @media (min-width: 992px) {
    
        .cropper-point.point-se {
          width: 10px;
          height: 10px
        }
      }
    
      @media (min-width: 1200px) {
    
        .cropper-point.point-se {
          width: 5px;
          height: 5px;
          opacity: .75
        }
      }
    
      .cropper-point.point-se:before {
        position: absolute;
        right: -50%;
        bottom: -50%;
        display: block;
        width: 200%;
        height: 200%;
        content: ' ';
        opacity: 0;
        /* #39f */
      }
    
      .cropper-invisible {
        opacity: 0;
      }
    
      .cropper-bg {
        background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
      }
    
      .cropper-hide {
        position: absolute;
    
        display: block;
    
        width: 0;
        height: 0;
      }
    
      .cropper-hidden {
        display: none !important;
      }
    
      .cropper-move {
        cursor: move;
      }
    
      .cropper-crop {
        cursor: crosshair;
      }
    
      .cropper-disabled .cropper-drag-box,
      .cropper-disabled .cropper-face,
      .cropper-disabled .cropper-line,
      .cropper-disabled .cropper-point {
        cursor: not-allowed;
      }
    
    </style>
    
    

    相关文章

      网友评论

          本文标题:vue 上传头像可裁剪

          本文链接:https://www.haomeiwen.com/subject/rjheektx.html