美文网首页
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 上传头像可裁剪

    install cropperjs 裁剪图片,利用getCroppedCanvas方法。 上传图片传参一般是for...

  • vue头像裁剪上传 vue-Cropper

    1.安装 vue-Cropper 2.main 全局引入或者局部引入vue-Cropper 3.直接上干货

  • AndroidHeadImageCliper

    GIT地址 功能描述:头像上传在APP中是很常见的功能,但是关于头像上传前的图片裁剪方式,如果使用系统提供的裁剪方...

  • Image

    设置头像 一、头像设置流程流程一般包括头像选择、上传、裁剪、预览四个步骤,UI界面至少给用户裁剪和预览。 1.选择...

  • Android实现头像上传

    Android实现本地上传图片并设置为圆形头像 Android实现类似换QQ头像功能(图片裁剪) android上...

  • (五)Node接口搭建——使用全球公认头像gravatar

    本节主要内容:头像处理 前提:在https://en.gravatar.com/注册账号,然后上传头像,裁剪选择p...

  • 头像裁剪上传功能

    头像裁剪上传功能在现在的App中基本都要用到,今天总结了一下,用比较简单实用的方式(调用系统API)来实现需求。这...

  • MVC 上传图片,裁剪头像

    头次在简书写东西,有什么问题多指教。 之前在做一个网站用户上传头像(可裁剪)功能,整理成文,如下: 1、选择插件,...

  • 前端必备的各种库

    Javascript 库 vue-cropper vue图片裁剪上传插件layabox 与白鹭齐名的h5游戏动画库...

  • vue图片裁剪组件me-image-crop

    头像的裁剪编辑功能是一般项目中的基础功能,所以使用vue开发了一个头像裁剪组件,使用npm安装。有需要的同学可以下...

网友评论

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

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