前言
图片合成的使用场景是非常广的,虽说强大的PS能将任意图片组合在一起,但它也只能实现固定的几张图片的合成,而Canvas则能借助drawImageAPI实现动态地将各种不同的图片合为一张图片。
使用场景
- 比如生成公司海报图,一般会先给出一个整体的宣传图,然后这张图片的某个位置需要填充公司一些社交app的二维码,这时候就可以借助canvas将不同的二维码合并到原始图上来生成不同的海报图。
- 我们要将用户的头像合到另一张图片上,这时候因为用户是不确定的,不同用户的头像不同,所以我们肯定不能用PS来做这件事,所以也可以借助canvas实现图片的合并。
canvas
<canvas>
是一个可以使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素。例如,它可以用于绘制图表、制作图片构图或者制作简单的动画。
drawImage方法
在canvas实例的2d上下文中有一个drawImage方法可以实现将图片源绘制到canvas内。 drawImage(image, dx, dy, dwidth, dheight);
image: 图像源实例。
dx: image
的左上角在目标画布上 X 轴坐标(也就是image的左上角从canvas画布的哪个x坐标开始)。
dy: image
的左上角在目标画布上 y 轴坐标(也就是image的左上角从canvas画布的哪个y坐标开始)。
dwidth: image
在画布上的宽度。
dheight:iamge
在画布上的高度。
drawImage方法也有其它参数,本文主要使用的就是上述几个参数也是比较常用的参数,其它参数说明。
具体实现
ini
// img1
<div>
<p>img1</p> <img id="img1" width="200" height="200" src="https://picsum.photos/200/200" alt="img1" title="img1">
</div>
// img2
<div>
<p>img2</p> <img id="img2" width="100" height="100" src="https://picsum.photos/100/100" alt="img2" title="img2">
</div>
// canvas
<p>canvas:</p>
<canvas id="canvas"></canvas>
// img3
<div>
<p>合并img1和img2后得图片</p>
<img id="img3" src="" alt="">
</div>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d'); // 获取canvas的2d上下文
const img1 = document.getElementById('img1');
const img2 = document.getElementById('img2');
const img3 = document.getElementById('img3');
// 设置canvas容器的宽高
canvas.width = img1.width;
canvas.height = img1.height;
// 设置图片资源允许跨域请求
img1.crossOrigin = "Anonymous";
img2.crossOrigin = "Anonymous";
// 等待img1资源加载完成后进行绘制
img1.onload = () => {
ctx.drawImage(img1, 0, 0, img1.width, img1.height);
// 等待img2资源加载完成后进行绘制
img2.onload = () => {
ctx.drawImage(img2, 20, 20, img2.width, img2.height)
// 获取canvas图片的dataURL
const url = canvas.toDataURL();
// 将url设置给img3的src
img3.src = url;
}
}
注意事项
1.从代码实现中我们看到给两个图片分别设置了crossOrigin = "Anonymous"
,这是因为当我们在 Canvas 中绘制了来自其他域的图片,并且图片的服务器未设置允许跨域资源共享 (CORS) 的头部时,就会导致 Canvas 被标记为"污染",从而无法通过 toDataURL
导出其内容。当我们不设置该字段时浏览器就会报错:
解决这个问题的方法通常是确保使用的图片与 Canvas 在同一个域下,或者在图片服务器上设置允许跨域资源共享 (CORS) 的头部。
就拿pcisum随机生成图片举例,这个随机生成图片的url对应的服务器已经设置了允许跨域资源共享,那么当我们把图片的crossOrigin属性设置为Anonymous时就表示告诉浏览器这个图片是允许进行跨域请求的并且不会携带任何凭证,那么浏览器再发起请求时就会再请求头中自动携带一个origin
字段设置值为null
,而服务器接收到请求后查看origin值为null说明这是一个匿名请求,服务器可以根据自己的 CORS 设置来处理该请求一般为添加响应头Access-Control-Allow-Origin: *(允许任何源进行跨域访问)
,浏览器拿到响应后拿到Access-Control-Allow-Origin来判断是否可以进行跨域。
但是如果服务器没有设置跨域资源共享 (CORS) 的响应头部时,那么无论我们是否设置crossOrigin
属性,浏览器再拿到服务器的响应后检查到响应头没有Access-Control-Allow-Origin
字段都会进行拦截,跨域请求失败。
2.crossOrigin
的另一个值use-credentials
,如果将图片的crossOrigin的值设置为该值,那么就表示该跨域请求是需要携带凭证的(如cookie或HTTP 认证信息),而且浏览器也会自动将请求头的origin字段设置为当前源的url,也会携带一些其他的凭证相关的请求头,这种情况下如果服务器Access-Control-Allow-Origin
值还为*,也会跨域失败,因为use-credentials
和 Access-Control-Allow-Origin: *
,这两者之间是互斥的。这种情况下只能将Access-Control-Allow-Origin
设置为确切的源。