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 绘制出来的图片背景颜色变成了黑色:
变成了
这是为什么呢?表面上是因为我将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 即可。
常见问题及错误
- 图片跨域问题:可以使用nginx设置一层代理(设置 CORS),或者代理到自己的服务器返回 base64 格式的数据。
- 背景色变为黑色:查看图片的格式是否正确。
- canvas生成的图片有白色的边框:查看canvas的尺寸大小是否和图片的大小一致,注意已定要等图片加载完成(onload 触发以后)之后再使用。
后续更新(2019-01-15): 服务器将第三方图片转换为base64时,遇到了严重的性能问题(后端PHP),故这个方法不可取。
后续更新(2019-01-16):通过查阅资料,图片转base64其实并不是那么消耗性能,导致性能的原因很可能是PHP在下载图片时使用 file_get_content 这个函数,它是没有缓存的。换成 curl 后就好了。
本文总结
以上就是使用canvas绘图并上传到服务器的所有关键点,若有错误支持,恳请指出;若有疑问之处,留言我会尽快回复。