欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

VUE框架下实现头像文件的截取和上传

程序员文章站 2022-03-08 15:58:41
...

VUE框架下实现头像文件的截取和上传

头像截取的需求

关于用户头像的选取有时候会遇到很多问题,比如图片的大小、比例等,有时候我们不免会想:如果用户上传的每一张头像都是固定的长和换、大小也不会有太大的区别就好了,很多网站、应用也都是这么做的。我们需要的就是一个固定大小的截取框,用来截取图片并上传就好了。其实实现这个需求也不算很难,昨天写了一个VUE框架下的demo,在这里分享出来~ 这个demo里用到的插件只有element-ui

实现思路

采用css的定位属性配合Canvas就足够实现这样的功能,我们需要两个嵌套在一起的div,外层用来显示画布,内层用来充当截取框,宽高、形状属性都可以设置为你所需要的头像的属性以达到一个预览效果,采用absolute定位,保证内部div框对外部div框的位置能够得到记录,在此基础上,在外部div里内嵌一个Canvas,同样采用absolute定位在左上角,此时,内部div相对外部div的位移同样也是它相对于同级Canvas的位移。我们把图绘制在这个Canvas上,然后设置一个缩放条能够缩放图片,截取的时候在内部div里新建一个和它等大的Canvas,再依照位置偏移把同一张图绘制在内部canvas里转化为base64,就可以直接上传给后端,或者显示到头像框里。

代码

<template>
  <div>
    <div class="avatar">
    <el-upload class="avatar-uploader" :before-upload="beforeAvatarUpload" action="#">
      <img v-if="showImg" :src="dataUrl" class="avatar">
      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
    </div>
    <div id="editBox">
      <canvas id="canvas"></canvas>
      <div id = "cutter" :style="getCutterPos()" @mousedown=drag($event)>
        <canvas id="inner"></canvas>
      </div>
      <div class="block">
        <span class="demonstration">缩放:</span>
        <el-slider class="slider" v-model="value" :min="minValue" :max="maxValue" :disabled="!hasImg"></el-slider>
      </div>
      <div class="ok" @click="showImage">确定</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "testImage",
  mounted(){
    this.initCanvas();
  },
  props:{
    
  },
  data() {
    return {
      minValue: 0,//缩放条的最小值,依据传入图片改动
      value: 0,//缩放条的值
      maxValue: 200,//缩放条的最大值,依据传入图片改动
      imageUrl: '',//所选图片的路径
      commitable: false,
      img: null,//图片文件
      hasImg:false,//画布上是否有图
      canvas: null,//外部canvas
      context: null,//外部canvas的ctx
      showImg: false,
      X: 0,//截取框位移
      Y: 0,//截取框位移
      type: '',//文件类型
      dataUrl: ''//base64
    };
  },
  watch:{
    value(val,oldVal){//动态缩放图片
      this.context.fillRect(0,0,750,450);
      var scale = val/100;
      this.context.drawImage(this.img,0,0,this.img.width*scale,this.img.height*scale);
    }
  },
  methods: {
    drag(ev){//截取框拖动,判定边界不超过外部canvas(750*450)
      var event = ev || event;
      var startX = event.clientX
      var startY = event.clientY
      var _that = this
      var PX = _that.X;
      var PY = _that.Y;
      document.onmousemove = function(ev){
        var event = ev || event;
        if(PX+event.clientX-startX>=0&&PX+event.clientX-startX<=600){
          _that.X = PX+event.clientX-startX;
        }
        if(PY+event.clientY-startY>=0&&PY+event.clientY-startY<=300){
          _that.Y = PY+event.clientY-startY;
        }
      }
      document.onmouseup = function () {
        document.onmousemove = null;
        document.onmouseup = null;
      }
      return false;
    },
    getCutterPos(){
      return "top:"+(this.Y-2)+"px;left:"+(this.X-2)+"px;"
    },
    initCanvas(){//初始化外部canvas
      this.canvas = document.getElementById("canvas")
      this.canvas.height = 450;
      this.canvas.width = 750;
      this.context = this.canvas.getContext("2d");
      this.context.fillStyle = "#FFF";
      this.context.fillRect(0,0,750,450);
    },
    beforeAvatarUpload(file) {
      const isJPG = file.type === 'image/jpeg'||file.type === 'image/png';//判定格式
      this.type = file.type;
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isJPG) {
        this.$message.error('上传头像图片只能是 JPG 或 PNG 格式!');
      }
      this.imageUrl = URL.createObjectURL(file)//创建图片链接
      this.startCanvas();
    },
    startCanvas(){//外部canvas开始工作
      this.hasImg = false;
      this.context.fillRect(0,0,750,450);
      if(this.imageUrl=='') return;
      this.img = new Image();
      this.img.src = this.imageUrl;
      var _that = this
      var canvas = _that.canvas
      var img = this.img
      this.img.onload = function(){
        _that.hasImg = true;
        var Max_height = canvas.height;//不论上传的图片多大,保证图片被包含在画布内,以此为依据更改缩放条的最大值
        var Max_width = canvas.width;
        _that.maxValue = (Max_width/img.width<Max_height/img.height?Max_width/img.width:Max_height/img.height)*100;
        _that.value = 100<_that.maxValue?100:_that.maxValue;
        var scale = _that.value/100;
        _that.context.drawImage(img,0,0,img.width*scale,img.height*scale);
      }
    },
    showImage(){
      this.hasImg = false;
      var canvas = document.getElementById("inner")
      canvas.height = 150;//内部canvas和截取框等大
      canvas.width = 150;
      var context = canvas.getContext("2d");
      var img = new Image();
      img.src = this.imageUrl;
      var _that = this
      img.onload = function(){
        var scale = _that.value/100;
        context.drawImage(img,-_that.X,-_that.Y,img.width*scale,img.height*scale);//实现截图
        _that.dataUrl = canvas.toDataURL(_that.type);//获取base64
        _that.showImg = true;//显示图片
        _that.hasImg = true;
        canvas.height = 0;
        canvas.width = 0;
      }
    }
  }
};//CSS一定要采用绝对定位!!!
</script>
<style scoped>
#canvas{
  position: absolute;
  left: 0px;
  top: 0px;
}
#editBox{
  border: skyblue solid 20px;
  background-color: skyblue;
  position: absolute;
  left: 400px;
  top: 100px;
  height: 500px;
  width: 750px;
}
.demonstration{
  position: absolute;
  bottom: 10px;
  left: 10px;
}
.slider{
  position: absolute;
  left: 70px;
  bottom: 0px;
  width: 550px;
}
.block{
  position: absolute;
  bottom: 0px;
  width: 700px;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
  border-radius: 50%;
}
.avatar {
  border: 1px solid black;
  width: 178px;
  height: 178px;
  display: block;
  border-radius: 50%;
}
.ok{
  cursor: pointer;
  position: absolute;
  right: 5px;
  bottom:-2px;
  background-color: skyblue;
  border-radius: 10px;
  border: 2px solid white;
  height: 40px;
  width: 90px;
  font-size: 20px;
  line-height: 40px;
  color: white;
}
#cutter{
  position: absolute;
  border: 2px solid red;
  height: 150px;
  width: 150px;
  border-radius: 50%;
}
#inner{
  position: absolute;
  left: 0px;
  top: 0px;
}
</style>

Demo展示和后话

VUE框架下实现头像文件的截取和上传
上传的时候直接给后端发去base64即可,回传数据可以和后端协商。

相关标签: 经验