Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

H5获取相册图片进行编辑裁剪上传 #43

Open
kekobin opened this issue Sep 25, 2019 · 0 comments
Open

H5获取相册图片进行编辑裁剪上传 #43

kekobin opened this issue Sep 25, 2019 · 0 comments

Comments

@kekobin
Copy link
Owner

kekobin commented Sep 25, 2019

基础知识

dataURL

一个完整的 dataURI 应该是这样的:

data:[<mediatype>][;base64],<data>

其中mediatype声明了文件类型,遵循MIME规则,如“image/png”、“text/plain”;之后是编码类型,这里我们只涉及 base64;紧接着就是文件编码后的内容了。我们常常在 HTML 里看到img标签的src会这样写:

src="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7"

这个img引用的就是以 base64 编码的 dataURL 了,只要浏览器支持,就可以被解码成声明格式的图片并渲染出来。

.toDataURL()

canvas.toDataURL([type, encoderOptions]);
  • type: 图片格式,默认为 image/png
  • encoderOptions: 在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。(这可以用于图片压缩,即指定质量为<1的就可以将原图片压缩到一定的比例)

图片加载是异步的,在转换成 dataURL 前必须先确保图片成功加载到,否则让 canvas 即刻执行绘制可能失败,从而导致转换 dataURL 失败。于是.toDataURL()方法应该写在的onload事件中,以确保 canvas 的绘制工作在图片下载完成后开始。好在.drawImage()方法是同步的,只有在 canvas 绘制完成后才会执行后续操作,比如.toDataURL()。

function getBase64(url){
        //通过构造函数来创建的 img 实例,在赋予 src 值后就会立刻下载图片,相比 createElement() 创建 <img> 省去了 append(),也就避免了文档冗余和污染
        var Img = new Image(),
            dataURL='';
        Img.src=url;
        Img.onload=function(){ //要先确保图片完整获取到,这是个异步事件
            var canvas = document.createElement("canvas"), //创建canvas元素
                width=Img.width, //确保canvas的尺寸和图片一样
                height=Img.height;
            canvas.width=width;
            canvas.height=height;
            canvas.getContext("2d").drawImage(Img,0,0,width,height); //将图片绘制到canvas中
            dataURL=canvas.toDataURL('image/jpeg'); //转换图片为dataURL
        };
    }

调取手机相册和相机

使用的是类似input[type=file]文件上传的形式,只不过在手机端会加上一些配置,从而能默认识别到相册 :

<form id="uploadForm" enctype="multipart/form-data">
      <input class="upload-open-photo" accept="image/*" type="file" id="filechooser" @change="btnUploadFile($event)"/>
    </form>

form-enctype: 设置或返回表单用来编码内容的 MIME 类型,默认值是"application/x-www-form-urlencoded",当 input type 是 "file" 时,值是 "multipart/form-data"。

处理上传的图片为dataURL

btnUploadFile(e) {
  // 获取图片
  const { files } = e.target;
  const file = files[0];
  const self = this;

  // 接受 jpeg, jpg, png 类型的图片
  if (!/\/(?:jpeg|jpg|png)/i.test(file.type)) {
    return;
  }

  const reader = new FileReader();
  reader.onload = function onload() {
    // 经过reader.readAsDataURL读取,这里的result为base64格式
    const { result } = this;

    let img = new Image();
    // 进行图片的渲染
    img.onload = () => {
      // 对图片进行压缩,返回的是压缩后的图片的base64
      const ret = self.compress(img, file.type);
      self.filename = file.name;
      // self.imgSrc = ret;
      // 对压缩后的图片进行裁剪
      self.initCroppie(ret);
      img = null;
    };
    img.src = result;
  };
  // 将file读取成dataURL格式
  reader.readAsDataURL(file);
}

对读取成dataURL的图片进行压缩

压缩处理步骤:

  • 利用 canvas 的 drawImage 将目标图片画到画布上
  • 利用画布调整绘制尺寸,以及导出的 quality ,确定压缩的程度
  • 利用 canvas的 toDataURL 或者 toBlob 可以将画布上的内容导出成 base64 格式的数据。
compress(img, fileType) {
      const drawWidth = img.width;
      const drawHeight = img.height;

      let canvas = document.createElement('canvas');
      canvas.width = drawWidth;
      canvas.height = drawHeight;

      let context = canvas.getContext('2d');
      // 先画到画布上
      context.drawImage(img, 0, 0, drawWidth, drawHeight);
      // 然后利用画布的功能进行压缩,压缩0.5就是压缩百分之50
      const base64data = canvas.toDataURL(fileType, 0.5);
      canvas = null;
      context = null;

      return base64data;
}

如果要调整图片方向的,可使用canvas的rotate:

const degree = 180;
drawWidth = -drawWidth;
drawHeight = -drawHeight;
context.rotate(degree * Math.PI / 180);

对压缩后的图片进行裁剪

使用的是Croppie 这个库实现放缩、移动、裁剪功能,但是其只支持相对或者绝对地址的图片url,所以先需要把我们的dataURI格式图片上传到服务器生成url。

dataURI转换成blob

由于上传接口只支持blob(file)格式,所以需要先把dataURI转换成blob:

// dataURI is base64 format
dataURItoBlob(dataURI) {
  // convert base64/URLEncoded data component to raw binary data held in a string
  let byteString;
  if (dataURI.split(',')[0].indexOf('base64') >= 0) {
  // 解码base64成为 binary string
    byteString = atob(dataURI.split(',')[1]);
  } else {
    byteString = unescape(dataURI.split(',')[1]);
  }
  // separate out the mime component
  const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

  // binary string没法转成blob的, 所以先转成ArrayBuffer形式,这里使用
  //Uint8Array形式,将binary string字符串存进去,然后就可以使用 new Blob转换了
  // write the bytes of the string to a typed array
  const ia = new Uint8Array(byteString.length);
  for (let i = 0; i < byteString.length; i += 1) {
    ia[i] = byteString.charCodeAt(i);
  }

  // Blob有兼容性问题,需要兼容处理
  let blob;
  try {
    blob = new Blob([ia], { type: mimeString });
  } catch (error) {
    const BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
    const bb = new BlobBuilder();
    bb.append([ia]);
    blob = bb.getBlob(mimeString);
  }
  return blob;
}

上传blob

async upload(base64) {
  const blob = this.dataURItoBlob(base64);
  const fd = new FormData();
  fd.append('filename', blob, this.filename);

  return uploadImg(fd);
}

实现裁剪

初始化

async initCroppie(base64) {
  // 现将dataURI转成blob进行上传(因为上传只支持blob格式),得到上传后的图片url
  // 因为裁剪使用的是Croppie,只支持相对或者绝对的图片url进行初始化
  const uploadRet = await this.upload(base64);
  const el = document.getElementById('croppie-container');

  this.croppieTarget = new window.Croppie(el, {
    viewport: { width: 100, height: 100 },
    boundary: { width: 300, height: 300 },
    showZoomer: false,
    enableOrientation: false // 这里注意,填false,否则默认图片会反过来
  });

  this.croppieTarget.bind({
    url: uploadRet.data, // 这里是图片的url
    orientation: 4
  });
}

裁剪可通过一个按钮触发

croppie() {
  this.croppieTarget.result('blob').then(async (blob) => {
    // do something with cropped blob
    console.log(blob);
    const fd = new FormData();
    // 注意,这里要把filename放在第三个参数带进去,因为blob里面是没有的
    fd.append('filename', blob, this.filename);

    const res = await uploadImg(fd);
    this.imgSrc = res.data;
  });
}

参考

利用 canvas 压缩图片
移动端 h5 照片的处理
HTML5调用照相机并自定义显示获取到的图片
用canvas的toDataURL()将图片转为dataURL(base64)
H5拍照上传填坑

@kekobin kekobin changed the title H5获取相册图片进行编辑上传 H5获取相册图片进行编辑裁剪上传 Sep 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant