最近一个产品需要是把微信服务号中一个网页内容生成一张图片,用户长按可以保存为图片,图片中的二维码可以识别,图片中包含用户头像。效果如图1-1
最常用的做法是把网页转换为cancas,接着转成图片,最流行的插件是html2canvas。官网的文档个人感觉不是特别好。具体做法参考了此博客。。
图1-1
红色圆圈位置应该为微信头像的位置,web端获取不到用红圈代替。右下角二维码是根据后端数据渲染出来的动态二维码。
整个需求难点和坑分为几部分:
1.获取头像并且可以保存到canvas生成的img中(canvas对于跨域图片的处理一般是在原图片处显示空白---需要后端配合)
2.生成canvas比生成二维码或微信头像快,导致二维码没有附在canvas生成的整图中(异步处理,安排事件执行顺序)
3.设置canvas宽高为背景大图带小数的原始尺寸(通过offsetWidth width等获得的是取整之后的宽度 )以及偏移尺寸。
4.图片清晰度处理(设置canvas画布缩放比)
1.处理canvas中跨域的问题,微信头像和自己的服务器必然不在同一个域,那首先需要前端设置canvas的配置项,允许跨域。网上有几种做法可以参考
1.1将微信头像上传到自己服务器,再返回前段图片地址,就不会涉及跨域问题。
1.2在NGINX上设置反向代理,把微信头像域名地址换成自己服务器的。(但是一旦微信头像更新域名这种做法就会出bug )参考资料
location ^~ /wechat_image/ { add_header 'Access-Control-Allow-Origin' "$http_origin" always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified- Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always; proxy_pass http://wx.qlogo.cn/;}复制代码
然后将微信的头像 => https://${自己的域名}/wechat_image/xxx和后端协商好下划线部分。
2.异步处理可以涉及很多内容,笔者自己也不是特别懂,此处不展开讲,下次专门出一篇异步的博客。此处处理将替换头像地址,生成动态二维码,canvas绘图按照前中后三步顺序执行,(没有做异步处理时,safiri中生成canvas比生成头像和生成二维码都快,导致最后的整图没有微信头像和二维码。)
3.生成canvas过程中会发现二维码的位置会在打开页面一秒左右往上方偏移一下,一开始以为是因为生成的画布以及图片和之前的dom节点高度不同,所以设置了画布高度时采用了getBoundingClientRect()获得实际dom元素宽高,以及生成画布过程中可能出现的偏移。
4.设置canvas的缩放scale。
代码如下:
// $(function() { function imgUrlChange(){ var wximg = $("#wximg").attr('src'); if(wximg){ var newimg = window.location.origin +'/wechat_image'+ wximg.split("http://thirdwx.qlogo.cn")[1]; }else{ console.log('微信头像没有读取到'); } $("#wximg").attr('src',newimg); } function shareQRCode () { return new Promise (function(resolve,reject){ setTimeout(function() { var shareid = $("#urldata").attr('uid'); var qrcode = new QRCode('qrcode', { text: 'your content', width: 90, height: 90, correctLevel: QRCode.CorrectLevel.H }); //使用 API qrcode.clear(); qrcode.makeCode(shareid); console.log(2); resolve(); },0) }) } function canvasImg (){ console.log(3) setTimeout(function(){ var cntElem = $('#html2canvas')[0]; var shareContent = cntElem;//需要截图的包裹的(原生的)DOM 对象 var cont = cntElem.getBoundingClientRect(); var width = cont.width; //获取dom 宽度 var height = cont.height; //获取dom 高度 var canvas = document.createElement("canvas"); //创建一个canvas节点 var scale = 2; //定义任意放大倍数 支持小数 canvas.width = width * scale; //定义canvas 宽度 * 缩放 canvas.height = height * scale; //定义canvas高度 *缩放 var context = canvas.getContext("2d"); context.scale(scale, scale); //获取context,设置scale context.translate(-cont.left,-cont.top);//设置context位置,值为相对于视窗的偏移量负值,让图片复位 var opts = { scale: scale, // 添加的scale 参数 canvas: canvas, //自定义 canvas // logging: true, //日志开关,便于查看html2canvas的内部执行流程 width: width, //dom 原始宽度 height: height, useCORS: true // 【重要】开启跨域配置 }; html2canvas(shareContent, opts).then(function (canvas) { // 【重要】关闭抗锯齿 context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; // 【重要】默认转化的格式为png,也可设置为其他格式 var image = canvas.toDataURL("image/png"); var pHtml = ""; $('#html2canvas').html(pHtml); $('#html2canvas>img').css({ "width": 100 + "%", "height":height, }); }, 200) }); } imgUrlChange() shareQRCode().then(function() { canvasImg() }) // })复制代码