h5: canvas操作图片并上传

canvas.drawImage()

作用:将图片绘制到canvas对象上,具体使用请参考 MDN文档
用法:

void ctx.drawImage(image, dx, dy); // 不缩放
void ctx.drawImage(image, dx, dy, dWidth, dHeight); // 设置缩放
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); // 对源图片进行裁剪

canavs.toDataURL()

作用:canavs.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。MDN文档

  • 如果画布的高度或宽度是0,那么会返回字符串“data:,”。
  • 如果传入的类型非“image/png”,但是返回的值以“data:image/png”开头,那么该传入的类型是不支持的。
  • Chrome支持“image/webp”类型。

用法:

canvas.toDataURL(type, encoderOptions);
// 这个方法有意思的是参数encoderOptions。在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

朋友们一定要注意了,encoderOptions 千万不能设置为1,除非你对图片的质量要求真的非常高,不然得到的数据会非常大。这里我使用一个大小为88k的图片做了一个测试:

  • 1.0 181k
  • 0.9 57k
  • 0.8 37k
  • 0.7 30k
  • 0.6 25k

图片内容绘制到canvas上

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui">
  <title></title>
  <link rel="stylesheet" href="">
</head>
<body>
  <img src="https://s1-1251010403.cosgz.myqcloud.com/20181227/images/wxacode_92d928a3eb8eb2b19e17ad16d86ab2bd.png">
</body>
<script>

var body = document.body;
var img = document.querySelector('img');
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

canvas.width = 400;
canvas.height = 300;

img.onload = function () {
  context.drawImage(img, 0, 0, 400, 300);
  console.log(canvas);
  body.appendChild(canvas);
}

// 这里需要注意的是当图片没有触发 onload 时, canvas将会读取不到内容
</script>
</html>

上面的代码完成了将图片绘制到 canvas 上的功能。主要依赖于 HTML5 canvas drawImage() 方法。

canvas对象转为 dataURL

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui">
  <title></title>
  <link rel="stylesheet" href="">
</head>
<body>
  <img src="wxacode_92d928a3eb8eb2b19e17ad16d86ab2bd.png">
</body>
<script>

var body = document.body;
var img = document.querySelector('img');
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

img.onload = function () {
  
  canvas.width = img.width;
  canvas.height = img.height;

  context.drawImage(img, 0, 0, canvas.width, canvas.height);
  var dataURL = canvas.toDataURL('image/jpeg');

  var nimg = new Image();
  nimg.src = dataURL;
  body.appendChild(nimg);
}
</script>
</html>

在这里我遇到了一个问题,canvas 绘制出来的图片背景颜色变成了黑色:

1303135-20181228093712602-1782051845.png

变成了

1303135-20181228094658616-510860734.png

这是为什么呢?表面上是因为我将png格式图片转换为jpeg格式的dataURL,其本质原因我尚不知晓。

压缩图片大小

压缩方式有两个方向:

  • 通过减少图片的尺寸,而减小图片所占的存储空间
  • canvas.drawImage 设置 转换系数
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }
  </style>
</head>

<body>
  <img class="bg" src="./01.jpg" onload="onImgLoad(this)">
</body>
<script>
  /**
   * 原图 750 * 380 88k
   * 处理后 375 * 190 22k
   * 由于图片的尺寸变小了,图片所占的存储空间也变小了
   */ 
  function onImgLoad (img) {
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');

    var width = img.width
    var height = img.height

    canvas.width = width / 2 // 宽度减半
    canvas.height = height / 2 // 高度减半
    
    context.drawImage(img, 0, 0, canvas.width, canvas.height);

    var dataURL = canvas.toDataURL('image/jpeg')
    
    var img = new Image()
    img.src = dataURL
    document.body.appendChild(img)
  }
</script>
</html>

上传图片到服务器

可以将 dataURL 数据转换为 File 文件格式进行异步上传。其中涉及到两个比较关键的技术。

(1)dataURLtoFile 实现

/**
 * base64 to File
 * @param {*} dataURL
 * @param {*} filename
 */
export const dataURLtoFile = (dataurl, filename) => {
  var arr = dataurl.split(',');
  var mime = arr[0].match(/:(.*?);/)[1];
  var bstr = atob(arr[1]);
  var n = bstr.length;
  var u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
};

(2)使用 FormData ajax 进行异步上传

const filename = `${uuid}_${stamp}.png`;
const file = dataURLtoFile(base64, filename);
const formData = new FormData();

formData.append('filecontent', file);
formData.append('op', 'upload');

底层原理是使用 POST 请求,可以直接将 FormData 实例作为请求body:

xmlHttpInstanceRequest.send(formData);

如果您是使用第三方ajax库,比如axios,$.ajax 等。只需将 data参数 设置 为 formData 即可。

常见问题及错误

  1. 图片跨域问题:可以使用nginx设置一层代理(设置 CORS),或者代理到自己的服务器返回 base64 格式的数据。
  2. 背景色变为黑色:查看图片的格式是否正确。
  3. canvas生成的图片有白色的边框:查看canvas的尺寸大小是否和图片的大小一致,注意已定要等图片加载完成(onload 触发以后)之后再使用。

后续更新(2019-01-15): 服务器将第三方图片转换为base64时,遇到了严重的性能问题(后端PHP),故这个方法不可取。
后续更新(2019-01-16):通过查阅资料,图片转base64其实并不是那么消耗性能,导致性能的原因很可能是PHP在下载图片时使用 file_get_content 这个函数,它是没有缓存的。换成 curl 后就好了。

本文总结

以上就是使用canvas绘图并上传到服务器的所有关键点,若有错误支持,恳请指出;若有疑问之处,留言我会尽快回复。

转载于:https://www.cnblogs.com/gaollard/p/10186581.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值