vue 上傳頭像可裁剪

install cropperjs 裁剪圖片男摧,利用getCroppedCanvas方法洛搀。

npm install cropperjs --save 
或者
yarn add cropperjs

上傳圖片傳參一般是form-data格式敢茁,form標(biāo)簽需要添加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>

需要注意的是:多次上傳同一個(gè)圖片,會(huì)導(dǎo)致兩個(gè)問(wèn)題:
1留美、點(diǎn)擊上傳選擇完圖片觸發(fā)不了裁剪界面彰檬;2、上傳完成請(qǐng)求接口成功后圖片未更新

解決1: change事件監(jiān)聽(tīng)value而觸發(fā)谎砾,而value在上傳文件的時(shí)候保存的是文件的內(nèi)容逢倍。需要在上傳成功的回調(diào)里面,將當(dāng)前input的value值置空即可景图。event.target.value=”;

解決2: 在new File的第二參數(shù)加上當(dāng)前時(shí)間戳就好啦

貼上完整代碼

<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 () {
    // 初始化這個(gè)裁剪框
    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: `圖片大小不允許超過(guò)${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({
      // 添加參數(shù)提升清晰度
        width: 800,
        height: 800,
        imageSmoothingQuality: 'high'
      })
      const roundedCanvas = this.getRoundedCanvas(croppedCanvas)
      // 如果不需要請(qǐng)求接口可直接將url更新到視圖
      // this.headerImage = roundedCanvas.toDataURL()
      // 接口接收f(shuō)ile內(nèi)容的blob對(duì)象
      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)  
      // 原圖過(guò)大導(dǎo)致上傳讀取慢较雕,截取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
    },
    // 觸發(fā)上傳
    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>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挚币,隨后出現(xiàn)的幾起案子亮蒋,更是在濱河造成了極大的恐慌,老刑警劉巖妆毕,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慎玖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡笛粘,警方通過(guò)查閱死者的電腦和手機(jī)趁怔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)湿硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人润努,你說(shuō)我怎么就攤上這事关斜。” “怎么了任连?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵蚤吹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我随抠,道長(zhǎng)裁着,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任拱她,我火速辦了婚禮二驰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秉沼。我一直安慰自己桶雀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布唬复。 她就那樣靜靜地躺著矗积,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敞咧。 梳的紋絲不亂的頭發(fā)上棘捣,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音休建,去河邊找鬼乍恐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛测砂,可吹牛的內(nèi)容都是我干的茵烈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼砌些,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呜投!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起存璃,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宙彪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后有巧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悲没,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年篮迎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了男图。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甜橱,死狀恐怖逊笆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情岂傲,我是刑警寧澤难裆,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站镊掖,受9級(jí)特大地震影響乃戈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亩进,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一症虑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧归薛,春花似錦谍憔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至千元,卻和暖如春苫昌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诅炉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工蜡歹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涕烧。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓月而,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親议纯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子父款,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345