此次开发活动时遇到了一些新的挑战,现将前端遇到的问题和相关解决方案记录如下,与诸位分享,希望能在以后的开发中减少踩坑的次数。
访问图片
-
图片的服务器地址跟当前页面地址不同,即跨域访问图片进行canvas绘图,首先服务器要允许其他服务器跨域访问资源,其次需要在前端对图片设置 img.crossOrigin=‘Anonymous’,才不会对画布造成污染
-
若访问的图片地址是二进制文件形式,则应该在img标签设置img.src=‘二进制接口地址’,而不能用json格式进行赋值,因为接口不返回json数据
-
应该等图片全部加载完再进行绘图操作,判断图片是否加载完成有以下几种方法:
- 通过img.onload事件
- 通过img.readystatechange事件,图片加载完时属性为:img.readyState=‘comlete’ || img.readyState=‘loaded’
- 通过img.comlete属性
-
然而图片是异步加载的,即使在canvas中设置img.οnlοad=drawImage(),也会出现画布上部分图片还未渲染出来的情况,如背景图和二维码有了,而头像还没绘制出来,因此应该对图片设置预加载,保证绘图时img已经全部加载完,可通过以下步骤进行预加载:
-
在html页面添加image标签,指定src为要加载的图片接口或地址
-
设置position:absolute,top:0,left:-9999px,z-index=-1,把图片放在看不到的地方(其实可以直接设置display:none;浏览器仍是会加载资源,只是不会渲染在页面上)
-
在loadCanvas方法中设置 let img= new Image(),img.src指向步骤a中的地址(浏览器请求资源时,都会先判断是否有缓存,若有缓存且未过期则会从缓存中读取,不会再次请求。先加载的图片会存储到浏览器缓存中,后面再次请求同路径图片时会直接读取缓存中的图片。)
-
绘制canvas
-
先获取canvas对象,let canvas = document.getElementById(‘canvasId’); let ctx = canvas.getContext(‘2d’),再利用ctx.drawImage()方法进行绘图,该方法的参数有以下几种情况:
-
ctx.drawImage(img, dx, dy),在画布的(dx, dy)位置绘制img对象
-
ctx.drawImage(img, dx, dy, dw, dh), 在画布的(dx, dy)位置绘制img对象,img对象宽高分别为dw 和 dh
-
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dy),在画布的(dx, dy)位置选取宽高分别为sw和sh,位于(sx, sy)位置的img对象,画布中img对象宽高分别为dw 和 dh,该方法常用于图像的缩放
-
-
用户头像常以圆形的形式绘制,方法如下:
-
先保存当前画布状态,ctx.save()
-
开始绘制圆形,ctx.arc(x, y, radius, 0, 2 * Math.PI)
-
将画布剪切,ctx.clip()
-
绘制头像,ctx.drawImage(img, dx, dy, dw, dy)
-
恢复画布, ctx.restore()
-
-
文本经常需要跨行显示,然而canvas没有自动换行的属性,需要自己计算长度跨行显示
-
设置各变量let x,y,maxWidth,lineHeight,arrText,line;分别对应文字显示的x坐标,y坐标,显示的最大宽度,行高(lineHeight = (that.canvas && parseInt(window.getComputedStyle(that.canvas).lineHeight)) || parseInt(window.getComputedStyle(document.body).lineHeight);),文字转化成的数组,每一行转换成的字符串
-
ctx.measureText(text);返回一个 TextMetrics 对象,包含关于文本尺寸的信息(例如文本的宽度)
for (let n = 0; n < arrText.length; n++) { let testLine = line + arrText[n]; let metrics = that.ctx.measureText(testLine); let testWidth = metrics.width; if (testWidth > maxWidth && n > 0) { that.ctx.fillText(line, x, y); line = arrText[n]; y += lineHeight; } else { line = testLine; } } that.ctx.fillText(line, x, y);
-
-
为保证canvas的绘制顺序,应该自底向上绘制图片,一个比较简单粗暴的做法就是多层onload嵌套
-
移动端还 不能直接导出canvas,所以最后应该将canvas转化成可识别的base64格式,然后赋值给img对象,canvas对象可在一开始就设置成display:none;
-
转化canvas时要注意图像已绘制完成,不然会转化成一张黑色背景的图片,可在onload方法等待图片绘制完成后再调用canvas.toDataURL(‘jpeg’)方法
以上便是对此次开发活动的分享,除了技术层面上遇到的问题,在与后台开发人员对接的过程中也要及时反馈,及早发现问题,才不会拖慢开发进度。