### [小程序用canvas绘制海报的做法](https://www.prosaas.cn/article/16623) **Published:** 2020-06-17T01:18:31 **Author:** 5201314 **Excerpt:** 2020年第一篇文章,年初忙着复习刷题一直没空去写东西,书看的越多感觉越技不如人,始终徘徊在小菜鸡的行列中,最… 2020年第一篇文章,年初忙着复习刷题一直没空去写东西,书看的越多感觉越技不如人,始终徘徊在小菜鸡的行列中,最近项目里正好有一个canvas的业务,突然又燃起了我一个UI前端的火种,记下了踩坑和思考。 ## 踩坑 ### 问题1:为什么在canvas上画图片模糊? 在canvas上绘制图片/文字的时候,我们设定canvas:375\*667的宽高,会发现绘制出来的图片很模糊,感觉像是一张分辨率很差的图片,文字看起来也会有叠影。 ![](https://img.php.cn/upload/article/000/000/051/9b18b104b923aa9857d906b6cf7849c7-0.png) > 注意:物理像素是指手机屏幕上显示的最小单元,而设备独立像素(逻辑像素)计算机设备中的一个点,css 中设置的像素指的就是该像素。 原因:在前端开发中我们知道一个属性叫`devicePixelRatio(设备像素比)`,该属性决定了在渲染界面时会用几个(通常是2个)物理像素来渲染一个设备独立像素。 举个例,一张100\*100像素大小的图片,在retina屏幕下,会用2个像素点去渲染图片的一个像素点,相当于图片放大了一倍,因此图片会变得模糊,这也是1px在retina 屏上变粗的原因。 ![](https://img.php.cn/upload/article/000/000/051/9b18b104b923aa9857d906b6cf7849c7-1.png) 解决: 将canvas-width和canvas-height都放大2倍,在通过style将canvas的显示width,height缩小2 倍. 例如: ### 问题2:如何处理px和rpx的转换? rpx是小程序里特有的尺寸单位,可以根据屏幕的宽度进行自适应,而在iphone6/iphonex上,1rpx等于不同的px。所以很可能会导致在不同手机下,你的canvas展示不一致。 在绘制海报的之前,我们拿到的设计稿一般都是基于iphone6的2倍图。而且从上一个问题的解决,我们知道canvas的大小也是2倍的,所以我们可以直接量取2倍图的设计稿直接绘制canvas,而尺寸需要注意一下rpxtoPx. /\*\*    \*     \* @param {\*} rpx     \* @param {\*} int  //是否变成整数    factor => 0.5 //iphone6    pixelRatio => 2 像素比    \*/ toPx(rpx, int) {     if (int) {       return parseInt(rpx \* this.factor \* this.pixelRatio)     }     return rpx \* this.factor \* this.pixelRatio   } ### 问题3:关于canvasContext.measureText计算纯数字的时候手机上为0 在小程序中提供`this.ctx.measureText(text).width`去计算文本的长度,但是如果你全`数字` 的话,你会发现该API永远都计算成0.所以,最后采用模拟measureText方法去计算文本长度。 measureText(text, fontSize = 10) {     text = String(text)     text = text.split('')     let width = 0     text.forEach(function(item) {       if (/\[a-zA-Z\]/.test(item)) {         width += 7       } else if (/\[0-9\]/.test(item)) {         width += 5.5       } else if (/\\./.test(item)) {         width += 2.7       } else if (/-/.test(item)) {         width += 3.25       } else if (/\[\\u4e00-\\u9fa5\]/.test(item)) { // 中文匹配         width += 10       } else if (/\\(|\\)/.test(item)) {         width += 3.73       } else if (/\\s/.test(item)) {         width += 2.5       } else if (/%/.test(item)) {         width += 8       } else {         width += 10       }     })     return width \* fontSize / 10   } ### 问题4:如何保证一行字体的居中展示?多行呢? 字体的如果过长,会超出canvas画布,造成绘制难看,这个时候我们就应该让超出的部分变成`...`你可以设置一个width并且循环计算计算出文本的宽度,如果超出则利用substring截取后补充`...`即可。 let fillText='' let width = 350 for (let i = 0; i <= text.length - 1; i++) { // 将文字转为数组,一行文字一个元素 fillText = fillText + text\[i\] // 判断截断的位置 if (this.measureText(fillText, this.toPx(fontSize, true)) >= width) {           if (line === lineNum) {             if (i !== text.length - 1) {               fillText = fillText.substring(0, fillText.length - 1) + '...'             }           }           if (line <= lineNum) { textArr.push(fillText) } fillText = '' line++ } else { if (line <= lineNum) { if (i === text.length - 1) { textArr.push(fillText) } } } } 文字剧中展示计算公式: 居中在canvas中可以用(canvas的宽度-文字宽度)/2 + x (x为字体的x轴的推移) let w = this.measureText(text, this.toPx(fontSize, true)) this.ctx.fillText(text, this.toPx((this.canvas.width - w) / 2 + x), this.toPx(y + (lineHeight || fontSize) \* index)) ### 问题5:在小程序中如何处理网络图? 关于在小程序里使用网络图片,比如cdn上的图片,是需要down到微信本地进行 LRU 管理,让后续绘制同样图片时,节省下载时间。所以首先需要你在微信小程序的后台配置downloadFile合法域名,其次你可以在canvas绘制之前,最好提前去down图片,等待图片下载好了,再开始绘制,以避免一些绘制失败的问题。 ### 问题6:在 IDE 中可设置 base64 的图片数据进行绘制,但真机上无用? 先把 base64 转成 `Uint8ClampedArray` 格式。然后再通过 `wx.canvasPutImageData(OBJECT, this)` 绘制到画布上,然后把画布导出为图片。 ### 问题6:如何画一个圆角图片? ### 问题7:关于wx.canvasToTempFilePath 使用 Canvas 绘图成功后,直接调用该方法生成图片,在IDE上没有问题,但在真机上会出现生成的图片不完整的情况,可以使用一个setTimeout来解决这个问题。 this.ctx.draw(false, () => {         setTimeout(() => {             Taro.canvasToTempFilePath({               canvasId: 'canvasid',               success: async(res) => {                 this.props.onSavePoster(res.tempFilePath)//回调事件                 // 清空画布                 this.ctx.clearRect(0, 0, canvas\_width, canvas\_height)               },               fail: (err) => {                 console.log(err)               }             }, this.$scope)           }, time)     }) ### 问题8:关于canvasContext.font fontsize 不能使用小数 如果设置 font 中字体大小部分包含小数,则会导致整个 font 设置无效。 ### 问题9:安卓下字体渲染错位? ![](https://img.php.cn/upload/article/000/000/051/9b18b104b923aa9857d906b6cf7849c7-2.jpg) 这个问题出现在安卓手机上,ios表现正常。一开始看到这个问题,摸不着头脑,为什么有的正常居中有的却往前了很多。后面发现是安卓下`this.ctx.setTextAlign(textAlign) `默认是为`center`,所以导致了错乱,改成left后就正常了。 ### 问题10:绘制一个折线图 ![](https://img.php.cn/upload/article/000/000/051/9b8347fbadc6ed622ca3de719e4e0eaf-3.jpg) 利用canvas绘制一个简单的折线图,只需要利用`lineTo`和`moveTo`俩个`API`将点连接即可。利用`createLinearGradient`绘制阴影。 ## 思考 ### 思考1:用json配置表生成海报的局限 现在的海报生成只需要按照设计稿去量取尺寸就可以,但是量取的过程还是很繁琐的,在设计稿量不到的地方还需要手动微调一下。 后续还可以做一个`web`端使用拖拽的方式去完成设计稿的事情,自动生成`json`应用到小程序的海报上。 ### 思考2:后端生成海报的局限 海报一开始是后端同学生成的,优点是不需要前端绘制时间,也不需要去踩微信API的坑,接口返回拿到url即可展示,但是在后端生成出来的效果不佳,毕竟这种工作更加前端一些。 ### 思考3:前端生成海报的局限 前端生成海报的时候我发现耗时更长,包括图片的下载本地而且还需要给安卓一个特意写一个setTimeout去确保绘制正常。各种兼容性问题、手机的dpr、安卓和ios等不间断彩蛋踩到你头秃~ 哈哈哈哈~ ## 彩蛋 ### 采用了最新的canvas-2d背景图确无法绘制全部? 在canvas开发的过程中,小程序里一直有一束微光提醒我。 ![](https://img.php.cn/upload/article/000/000/051/9b8347fbadc6ed622ca3de719e4e0eaf-4.png) 我也试了试最新的canvas2d的api,的确同步了web端,写法也更流畅,在开发者工具中看是一切正常,跑在手机上则,只显示宽度的一半在各种机型下测试也是一样。 ![](https://img.php.cn/upload/article/000/000/051/9b8347fbadc6ed622ca3de719e4e0eaf-5.jpg) 后面改成原始的canvas就又好了。。。具体原因也还没有在微信社区里找到,后续迭代升级的时候再研究阿吧啊吧啊吧。 ![](https://img.php.cn/upload/article/000/000/051/9b8347fbadc6ed622ca3de719e4e0eaf-6.jpg) 推荐教程:《JS教程》 以上就是小程序用canvas绘制海报的做法的详细内容,更多请关注知企PROSAAS其它相关文章! **Categories:** 后端开发 ---