canvas 踩坑

此次开发活动时遇到了一些新的挑战,现将前端遇到的问题和相关解决方案记录如下,与诸位分享,希望能在以后的开发中减少踩坑的次数。

访问图片

  1. 图片的服务器地址跟当前页面地址不同,即跨域访问图片进行canvas绘图,首先服务器要允许其他服务器跨域访问资源,其次需要在前端对图片设置 img.crossOrigin=‘Anonymous’,才不会对画布造成污染

  2. 若访问的图片地址是二进制文件形式,则应该在img标签设置img.src=‘二进制接口地址’,而不能用json格式进行赋值,因为接口不返回json数据

  3. 应该等图片全部加载完再进行绘图操作,判断图片是否加载完成有以下几种方法:

    • 通过img.onload事件
    • 通过img.readystatechange事件,图片加载完时属性为:img.readyState=‘comlete’ || img.readyState=‘loaded’
    • 通过img.comlete属性
  4. 然而图片是异步加载的,即使在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

  1. 先获取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,该方法常用于图像的缩放
      在这里插入图片描述

  2. 用户头像常以圆形的形式绘制,方法如下:

    • 先保存当前画布状态,ctx.save()

    • 开始绘制圆形,ctx.arc(x, y, radius, 0, 2 * Math.PI)

    • 将画布剪切,ctx.clip()

    • 绘制头像,ctx.drawImage(img, dx, dy, dw, dy)

    • 恢复画布, ctx.restore()

  3. 文本经常需要跨行显示,然而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);
      
  4. 为保证canvas的绘制顺序,应该自底向上绘制图片,一个比较简单粗暴的做法就是多层onload嵌套

  5. 移动端还 不能直接导出canvas,所以最后应该将canvas转化成可识别的base64格式,然后赋值给img对象,canvas对象可在一开始就设置成display:none;

  6. 转化canvas时要注意图像已绘制完成,不然会转化成一张黑色背景的图片,可在onload方法等待图片绘制完成后再调用canvas.toDataURL(‘jpeg’)方法

以上便是对此次开发活动的分享,除了技术层面上遇到的问题,在与后台开发人员对接的过程中也要及时反馈,及早发现问题,才不会拖慢开发进度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值