vue 基于canvas的编辑图片并上传至服务器。只要两碗双皮奶的时间,你上你也行

来,效果图

 1、实现思路

       先通过浏览器打开本地图片,再用canvas加载图片,然后通过getImageData把canvas的画布输出,然后用你想裁剪成多大的另一个canvas通过putImageData按照对应的画布位置接收然后将这个canvas用toDataURL输出base64的图片然后在把这个base64转化成图片文件就可以进行上传了。

2、基础实现

  • 文件结构

文件我是放在 components 里面(就是随便一个放组件的位置)

  • 代码实现




      上面是全部的代码,要贴的话,只管这个就行。下面为部分核心代码段说明

        如何加载本地图片就不多说了,首先是如何canvas移动图片,无非就是获取鼠标的坐标位置(clientX)减去最外面的元素(position)的坐标位置减去自身元素位置的偏移(150,为什么是150呢,我样式是这么写的),然后减去上mousedown事件点下去鼠标的初始化坐标位置(ponitX)这个,这样得出来值就是就是让图片实际偏移的位置了。(ps:如果有更好的拿到这种位置的方法请务必告诉我,谢谢啦)下面是计算位置的代码:

 //编辑图片
editImg(){
  let e = event ? event : window.event;
  let position = this.$refs.myImgTailoring;
  if(this.dragFlag){ //是否拖动
     this.valueX = e.clientX - position.offsetLeft - 150 - this.ponitX;
     this.valueY = e.clientY- position.offsetTop - this.pointY;
     this.loadingImg(this.valueX, this.valueY, 550, this.zoom)
   }
}

这个是大概的结构图

vue 基于canvas的编辑图片并上传至服务器。只要两碗双皮奶的时间,你上你也行_第1张图片

    放大就是监听滚轮事件按照一定的比例缩放缩放图片的宽度,然后高度按照宽度的比例进行等比例缩放就实现放大了。具体实现如下面代码:  

  • 监听滚轮

    这个监听滚轮的代码,网上一大堆 。

//。。。。。省略部分代码
var scrollFunc = (e) => {
    if (e.wheelDelta) {  //判断浏览器IE,谷歌滑轮事件
       if (e.wheelDelta > 0) { //当滑轮向上滚动时
          this.zoom ++;
       }
       if (e.wheelDelta < 0) { //当滑轮向下滚动时
          this.zoom --;
       }
     } else if (e.detail) {  //Firefox滑轮事件
       if (e.detail> 0) { //当滑轮向上滚动时
          this.zoom ++;
       }
       if (e.detail< 0) { //当滑轮向下滚动时
         this.zoom --;
       }
     }
};


//滚动滑轮触发scrollFunc方法
window.onmousewheel = document.onmousewheel = scrollFunc;
  • 放大    
width = width * (1 + zoom *0.02);//放大倍率
let showHeight = width / img.width * img.height; //根据宽度等比例缩放高度

  上面这些弄完后通过擦除canvas画布,然后写入新的画布实现动画效果。

//载入图片
//saveflag 是否保存坐标
//centerFlag,高度是否居中
//scollFlag, 是否为滚轮放大
loadingImg(x, y, width, zoom, centerFlag, saveFlag, scollFlag){
  let canvas = this.$refs.myImgTailoringCanvas;
  canvas.width = 550;
  canvas.height = 400;
  let ctx = canvas.getContext('2d'); 
  let img = new Image();
  img.src = this.imgData;
  img.onload = () =>{
    //清空画布
    ctx.clearRect(0, 0, 550, 400);
    // 将图片画到canvas上面上去!
    width = width * (1 + zoom *0.02);//放大倍率
    let showHeight = width / img.width * img.height; //根据宽度等比例缩放高度
    let showTop = centerFlag ? 200 - showHeight/2 : this.imgY + y;//计算偏移
    x += this.imgX;//计算偏移
    if(saveFlag){//不再拖拽时更新最后的显示坐标
      this.imgX = x; 
      this.imgY = showTop;
    } 
    if(scollFlag){ //放大不计算偏移
      ctx.drawImage(img, this.imgX, this.imgY, width, showHeight);
      return
    }
    ctx.drawImage(img, x, showTop, width, showHeight);
  }
},

 为什么要在img.onload里面进行操作呢,因为你执行完了,图片都未必加载完,所以在这里是最稳妥的。

  • 裁剪实现

就是按照上面的思路,先把显示图片的canvas的画布输出,然后用另一个canvas去按照辅助线的位置进行接收,这样就实现了裁剪。

saveImg(){
  if(this.imgData == ''){
    return
  }
  let canvas = this.$refs.myImgTailoringCanvas;
  let tempCanvas = this.$refs.myImgTailoringTempCanvas
  let line = this.$refs.myImgTailoringLine;
  tempCanvas.width = this.imgWidth;
  tempCanvas.height = this.imgHeight;
  let ctx = canvas.getContext('2d');
  let tctx = tempCanvas.getContext('2d');
  //截取框里面的内容
  tctx.putImageData( ctx.getImageData(line.offsetLeft - this.imgWidth/2, line.offsetTop - this.imgHeight/2, this.imgWidth, this.imgHeight), 0, 0)

}

这样一来,我们就有tempCanvas截取后的画布了,到这里,几乎已经结束了。可以为所欲为。嘿嘿嘿。把这个canvas输出base64,然后将base64转化为文件就得到裁剪后的图片了。代码如下

  •  输出base64
let base64Img = tempCanvas.toDataURL("image/png");
  • base64转文件

这个base64转文件代码是我在网上找的,来源为 https://www.jianshu.com/p/e26aaca7e201十分感谢

//base64格式转换为img文件
dataURLtoFile(dataurl, filename = 'file') {
  let arr = dataurl.split(',')
  let mime = arr[0].match(/:(.*?);/)[1]
  let suffix = mime.split('/')[1]
  let bstr = atob(arr[1])
  let n = bstr.length
  let u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], `${filename}.${suffix}`, {
    type: mime
  })
}
  • 上传文件到服务器

       这个功能的实现,我是模仿elementUI的上传图片的格式进行实现的,他主要接收参数是 一个是上传文件的url,一个上传前的方法,一个上传成功后的方法。因为这里有了验证,所以不需要上传前的方法(当然,你还是想要,你可以自己在这个基础上添加就行)。参数如下

props:{
  show: { //显示
    default: true, 
    type: Boolean,
  },
  imgWidth: { //要截取的图片宽度
    default: 200,
    type: Number
  },
  imgHeight: { //要截取的图片的高度
    default: 200,
    type: Number
  },
  url: { //图片 上传路径
    default: '', //user, pwd,
    type: String,
  },
  onSuccess: { //成功后执行的函数
    type: Function
  }
},

 然后是上传方法。通过axios来实现

uploadImg(file){
  var forms = new FormData();
  forms.append('file',file)
  let config = {
    headers:{'Content-Type':'multipart/form-data'}
  };
  this.loading = true;
  axios.post(this.url, forms, config)
  .then(res=>{
    this.loading = false
    this.$emit('close', false);
    this.onSuccess(res);  
  })
  .catch(err => {
    this.loading = false;
    //补充异常处理代码
    console.log(err)
  })
},

 

如何使用

得终于到了这个换接,使用方法很简单的参考elementUI的就行,代码如下:

引入:

import myImgTailoring from '@/components/myImgTailoring/myImgTailoring.vue'

使用:

 //上传成功后调用的方法

 最后

       因为这个是基于canvas开发,所以在此基础上你还可以添加自定义文字啊,图案啊。是不是很过瘾。你可能会问,回显呢?哪个自己做吧,参考elementUI,因为该插件执行了你的上传成功方法,想怎么回显就怎么回显。以上,我上传图片的图片素材和部分代码来源于网上,如果素材或者代码引起侵权,我将删除次文章。感谢各位阅读。

你可能感兴趣的:(vue,canvas,前端)