Featured image of post canvas 图片跨域问题

canvas 图片跨域问题

前言

先祝大家圣诞节快乐 🎅🎁 🎄

最近公司也是趁圣诞节想出个活动:给用户测试属性和圣诞专属头像的活动。并把结果生成为图片保存下来 :huaji: 。

开发中图片与网页的域名不同,造成了很多问题在此记录。

  • 图片 xhr 跨域加载报错
  • 字体跨域绘制时不能正常使用字体
  • 绘制了 canvas 但不能调用 toDataUrl 方法

填坑开始了 :doge: :

问题描述

图片加载跨域

1. 报错信息

canvas图片跨域问题-2023-11-30-14-04-58

原因

🙈 图片跨域禁止加载(xhr 方式)。

2. toDataURL 无法调用

1
2
Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
'xxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

canvas图片跨域问题-2023-11-30-14-05-04

原因

canvas 中如果绘制了外部(其它域名下)的图片,则 canvas 会被标记为 “不安全” 的这个时候 canvas 会禁止调用诸如: toDataURL , toBlob 方法 🐒 。

解决方式

服务端配置

1
Access-Control-Allow-Origin: *

或者配置成你需要指定的域名

1
Access-Control-Allow-Origin: www.baidu.com

js 中

js 中加载图片的时候设置 crossOrigin* 或者 anonymous 表示为希望使用跨域图片

以加载库 preload-js 为例:

1
this.queue = new createjs.LoadQueue(true, "", "anonymous");

如果你使用的是 image 加载的:

1
2
3
let img = newImage();
img.setAttribute('crossOrigin', 'anonymous');
img.src = images[i].url;

结束

配置完这两步就已经解决了上述问题。

才怪

如果你觉得这样就完了那就太简单了。
下面要来一些不一样的。 :trollface:

如果现在图片跨域了,但是你还是想绘制到 canvas 上怎么办呢?

也有办法,但你要注意几点: 👀

  • 图片 CDN 中存在
  • 无网络字体使用
  • 不需要能将 canvas 转为图片数据

如果你的项目能接受上面的条件,那么方法来了:

要先从跨域原因说起

请求类型 img, xhr 的请求时的区别

img 指的是 png, jpg, gif 等

img: 请求允许跨域,不受浏览器同源策略的影响
xhr: 默认不允许跨域,受浏览器跨域限制

图片跨域

知道了当加载类型为 img 时~其实图片是不存在跨域情况的。那么之所以你请求的图片跨域了,其主要原因是使用了 xhr 加载的图片。

canvas图片跨域问题-2023-11-30-14-05-12

图片是服务器已经配置过 Access-Control-Allow-Origin 的,所以加载成功了

那怎么样改成 img 类型呢?

继续绘制

1
2
3
4
let img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = "url.png";
img.onload = ()=>{};

这就是 img 类型的加载事例。

很多图片加载库默认会使用:xhr 方式加载

同样以 preload-js 为例来使用 img 类型:

1
2
this.queue = new createjs.LoadQueue(true, "", "anonymous");
this.queue.useXHR = false; // 设置不使用 `xhr` 加载

这个时候你在去看控制台(network)时就会发现,图片全是 png,jpg,gif 了,而且 Access-Control-Allow-Origin 此类的报错也没有了。

结果

细心的你可能已经看到:上面问题描述里第二条中的图片 canvas 里就已经把图片画出来了。

简单几步 canvas 中继续绘图显示就没有问题了~

但最后还是要提醒一下,

  1. 网络字体没办法解决,只是用最上方的解决方法
  2. canvas 不能转为图片数据 toDataURL 不能调用

真的结束

到这里就真的是结束了。 :trollface: