上传头像

671 阅读3分钟

具体样式 头像上传.gif

功能

  1. 上传文件: 上传文件并保存文件数据
  2. 预览:将上传的文件转换成图片地址,显示在页面上
  3. 截图:固定选择框背景,然后将图片数据放置到canvas中进行移动放大然后进行裁切
  4. 下载:将裁切的数据进行转换,转成可以下载的图片格式,进行下载

视图代码

<template>
  <div class="avatar">
    <a-button class="btn">
      <span>上传头像</span>
      <input type="file" name="" id="upload_file" @change="handleChange">
    </a-button>

    <!-- 预览和剪切的弹框-->
    <a-modal v-model="visible" title="图片预览或编辑" @ok="handleOk"              class="avatar_modal">
      <div class="main">
        <img :src="imgSrc" alt="" v-if="isView">
        <template  v-else>
          <div>
            <!-- 滑块放大图片-->
            <input id="slider" type="range" max="4" min="1" v-model="currentSize" @input="handleRange"/>
          </div>
          <div id="demo"> 
            <canvas id="myCanvas" width="400px" height="400px" ref="canvas"></canvas>
            <canvas id="myImg"  width="400px" height="400px" ref="myImg"></canvas>
          </div>
        </template>
      </div>
      <div class="model_btn">
        <a-button style="margin-right:20px" @click="handleView('VIEW')">预览</a-button>
        <a-button @click="handleView('CLIP')">截图</a-button>
      </div>
    </a-modal>
  </div>
</template>

data数据和props数据代码

data(){
  return {
    visible:false, // 打开预览和截图弹框
    isView:true, // 切换预览还是截图
    imgSrc:'', // 预览图片地址
    currentSize:1, // 图片放大倍数(1-4)
    move:{ // canvas中图像的运动的路径数据
      lastMoveX:null,
      lastMoveY:null,
      currentMoveX:null,
      currentMoveY:null,
      isMoving:false
    },
    canvas_edit:{ // 被编辑的图片canvas
      el:null, //canvas元素
      ctx:null, // canvas的2d环境
      image:null, // canvas图像元素
      left:0, // canvas距离父元素的左侧的宽度
      top:0 // canvas距离父元素顶部的高度
    },
    blackMask:{ // 选择框的canvas元素数据
      el:null,  // canvas元素
      ctx:null  // canvas的2d环境
    }

  }
},
props:{
  // 限制的上传格式
  limited:{
    default:()=>{return ['image/png']},
    type:Array
  }
},

文件上传和图片预览

handleChange(e){
  let file_dom = document.getElementById('upload_file')
  let file = e.target.files[0]
  if(this.limited.includes(file.type)){
    this.imgSrc = window.URL.createObjectURL(file) 
    this.visible = true
  }else{
    let str = this.limited.reduce((total,currentValue) =>{ return  total + currentValue.split('/')[1] + '、'} ,'')
    this.$message.warn('上传文件格式错误,仅支持'+str)
  }
  file_dom.value = null
}

描述:

  1. 获取上传的文件file
  2. 判断file的 格式,不合格给予提示
  3. file转换成可以显示的图片地址imgSrc
  4. 将图片显示到对应的节点上实现预览
  5. 清除上传文件的缓存

初始化mask和canvas图片

drawCanavs(){
  this.$nextTick(() =>{
    this.canvas_edit.el = this.$refs.canvas // 这个是操作图片的canvas
    this.canvas_edit.ctx = this.canvas_edit.el.getContext('2d')
    this.canvas_edit.image = new Image()
    this.canvas_edit.image.src = this.imgSrc
    this.canvas_edit.image.onload = () =>{
      this.initCanvas() //初始化canvas元素
    }

    // 初始化mask的元素
    this.blackMask.el = this.$refs.myImg
    this.blackMask.ctx = this.blackMask.el.getContext('2d')
    this.blackMask.ctx.fillStyle = 'rgba(0,0,0,0.3)'
    this.blackMask.ctx.fillRect(0,0,400,400)
    this.blackMask.ctx.clearRect(100,100,200,200) 

    // 由于无法直接在canvas_edit.el元素上监听到数据,可以选择在mask中进行
    this.blackMask.el.onmousedown = (e) =>{
      this.move.isMoving = true
      this.move.lastMoveX = e.clientX
      this.move.lastMoveY = e.clientY

    }
    this.blackMask.el.onmousemove = (e) =>{
      if(this.move.isMoving){
        let width = e.clientX -  this.move.lastMoveX + this.canvas_edit.left
        let height = e.clientY -  this.move.lastMoveY+this.canvas_edit.top
        this.drawCanvas(width,height)
      }
    }
    this.blackMask.el.onmouseup = (e) =>{
      if(this.move.isMoving){
        this.move.isMoving = false
        let width = e.clientX -  this.move.lastMoveX + this.canvas_edit.left
        let height = e.clientY -  this.move.lastMoveY+this.canvas_edit.top
        this.drawCanvas(width,height)
        this.canvas_edit.left = width
        this.canvas_edit.top = height
      }
    }
  })
},

// 绘制居中绘制图片到canvas图像
initCanvas(){
  let width = (400 - this.canvas_edit.image.width*this.currentSize)/2 //图片的初始坐标(width,heigth)
  let height = (400 - this.canvas_edit.image.height*this.currentSize)/2
  this.canvas_edit.left = width
  this.canvas_edit.top = height
  this.canvas_edit.ctx.drawImage(this.canvas_edit.image,width,height,this.canvas_edit.image.width*this.currentSize,this.canvas_edit.image.height*this.currentSize)
},
// 更新canvas图像
drawMask(width,height){
  this.canvas_edit.ctx.clearRect(0,0,400,400)
  this.canvas_edit.ctx.drawImage(this.canvas_edit.image,width,height,this.canvas_edit.image.width * this.currentSize,this.currentSize * this.canvas_edit.image.height)
},

描述:

  1. 绘制图像到canvas图像中
  2. 绘制遮罩层
  3. 在制遮罩层中绑定按下、移动、按上的事件
  4. 在按下的时候,确定好当前的鼠标所在的位置
  5. 在移动的时候,根据鼠标移动的距离来移动canvas图像中图片移动的位置,对canvas图像进行更新
  6. 在按上的时候,确定鼠标离开时的位置做为canvas图像中图片的坐标,并更新图像

剪裁和下载

handleOk(){
  // 获取裁切到的图像数据
  let image_data = this.canvas_edit.ctx.getImageData(100,100,200,200) 
  this.canvas_edit.ctx.clearRect(0,0,400,400)
  
  // 将canvas修改成和选中图像一样大的宽高
  this.canvas_edit.el.height = 200
  this.canvas_edit.el.width = 200
  this.canvas_edit.el.style.left = '100px'
  this.canvas_edit.el.style.top = '100px'
  this.canvas_edit.ctx.putImageData(image_data,0,0) // 将裁切到的元素放到canvas中

  // 将canvas图像转换成可下的图片格式,并下载
  let base64_img = this.canvas_edit.el.toDataURL('image/png')
  let blob_img = this.dataURLToBlob(base64_img)
  let url = URL.createObjectURL(blob_img)
  let a = document.createElement('a')
  a.setAttribute('download', '头像数据' + '.png')
  a.setAttribute('href',url)
  a.click()
  this.visible = false
  this.$emit('getUrl',url)
}
// base64转换成Blob对象
dataURLToBlob(code) {
  let parts = code.split(';base64,')
  let contentType = parts[0].split(':')[1]
  let raw = window.atob(parts[1])
  let rawLength = raw.length
  let uInt8Array = new Uint8Array(rawLength)
  for(let i = 0; i < rawLength; ++i) {
    uInt8Array[i] = raw.charCodeAt(i)
  }
  return new Blob([uInt8Array], {
    type: contentType
  })
},

描述

  1. 获取canvas图像中被选中的图像数据
  2. 清屏,修改canvas元素的大小,并将canvas图像放置到中心
  3. 将获取到的图像数据放到canvas元素中
  4. 将canvas转换成可下载的图像进行下载