奥利奥生成器与Canvas绘制图片不显示问题
最近尝试在 Vue 中实现奥利奥生成器的效果,遇到使用 Canvas 的 drawImage 方法绘制图片不显示的问题,在此记录下解决过程。
前言
欢迎体验
奥利奥生成器
实现思路
奥利奥的原理,就是根据不同的字符串组合,对应不同的图片,然后按顺序将图片绘制到 canvas 上,最终生成图片。
canvas 的 drawImage 有几种用法。
- 在画布上定位图像:
ctx.drawImage(img,x,y);
- 在画布上定位图像,并规定图像的宽度和高度:
ctx.drawImage(img,x,y,width,height);
- 剪切图像,并在画布上定位被剪切的部分:
ctx.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
我们这里只需要使用第二种方法,控制下图片的大小即可。
参数值
参数 | 描述 |
---|---|
img | 规定要使用的图像、画布或视频。 |
sx 可选。 | 开始剪切的 x 坐标位置。 |
sy 可选。 | 开始剪切的 y 坐标位置。 |
swidth 可选。 | 被剪切图像的宽度。 |
sheight 可选。 | 被剪切图像的高度。 |
x | 在画布上放置图像的 x 坐标位置。 |
y | 在画布上放置图像的 y 坐标位置。 |
width 可选。 | 要使用的图像的宽度(伸展或缩小图像)。 |
height 可选。 | 要使用的图像的高度(伸展或缩小图像)。 |
一般情况下,都按照下面的示例来使用。把绘制写到 img 的 onload 事件里,是因为如果图片还没有加载完成,drawImage 是不生效的,这样能够保证图片已经加载完成。不过因为我们需要多次调用同一张图片,绘制多次,这样的方法就不是很方便。
1 | var c = document.getElementById("myCanvas"); |
于是尝试将加载的图片缓存一下,方便调用,使用的是下面的方法。sources 中是图片名称和图片地址的键值对,使用 Image 对象设置 src 的方式加载图片;在 onload 事件中,记录加载完成的次数;当全部加载完成时,将存储了这些 Image 对象的 cacheImages 对象存储下来以便调用。
在原版 oreooo 的页面中,我可以看到这个方法是有效的。但是我在 Vue 3 + Vite 2 的环境下,虽然 cacheImages 成功存储了,也能在 Networks 里面看到网络请求,但是 canvas 绘制出来的图形一直是空白的。
1 | loadImages: function (sources, callback) { |
或许是需要实际存在的 img 标签才有用,我如果页面上实际就有这个图片,我测试了下,确实就能获取到图片绘制出来了。于是我换了种写法,通过在页面实际加载图片,然后在绘制时获取图片 DOM 的 image,只要图片已经加载完成了,绘制时就能正常出图了。下面是在 Vue 里的一个大概示例。
1 | <template> |
总结
Canvas 的 drawImage 在图片还未加载完成时是不会生效的,由于这个特性,需要保证在调用 drawImage 时图片已经加载完成,否则就会有不显示的现象。解决的方案有下面两种。
- 将 drawImage 事件写在图片的 onload 事件中,这样可以保证图片已经加载。
- 可以在页面上写上 img 标签实际加载图片,然后 drawImage 时调用 img 的 DOM 下的 image 属性进行绘制。