最近遇到一个业务需求,需要在小程序端自定义预览功能,在预览的图片中使用指定的外部字体。将预览图片上传到OSS,后台生成PDF,在管理系统下载。但是……经过实践发现,虽然小程序做了分包处理,但是还是不能把字体包存到本地,把字体放在OSS上返回,但是存在跨域,虽然配置了允许跨域,还是不行。和!!!小程序的canvasAPI无法设置字体,h5中canvas中没有context.font='fontname'方法。最终还是决定曲线救国,放弃小程序端预览和生成canvas的功能,在管理系统中导入canvas为字体,生成图片等操作,使用原生canvas来实现。技术要点Canvas文字排版Canvas设置指定背景颜色Canvas导入外部字体Canvas绘制文字图片将canvas生成的base64图片转成文件上传(这里根据后台协商,这里是后台要求)生成图片为PDF,点击批量下载实现步骤Canvas文本排版在一般的HTML容器中,很容易实现文本排版。例如:实现文本超过容器宽度自动换行,默认文本超过容器宽度自动换行,也可以使用word-wrap:break-word实现强制换行。实现文本垂直排列有几种方式:设置文本容器的writing-mode样式:(存在兼容性问题)writing-mode:vertical-rl;//垂直方向从右向左书写。即top-bottom-right-left或writing-mode:vertical-lr;//垂直方向的内容从上到下,水平方向的内容从左到右。具体效果如图:但是这对浏览器也有一定的兼容性问题,使用时需要注意。使用宽度控制换行:(不兼容,推荐方法)设置每行宽度为一个word大小,使用文本超出默认换行的特性,或者设置强制换行超出限制,实现竖排文本.使用br标签实现或者为每段文字存储一个标签,实现换行:(很死板的写法,比较low,不推荐)在每段文字后加br标签,或者给每段文字放一个标签,这样写的灵活性就没有了高的。非常不推荐!实现canvas中的文本布局,实现横向文本布局。在canvas中,如果文字超出canvas的大小,不会自动换行,会在超出部分的正后方继续画一行。canvas中没有可以直接设置换行的API,那么如何实现换行呢?可以通过js控制,通过计算当前绘制文本的x坐标,如果x坐标大于画布的宽度,则将x坐标赋值为0(画布起点的x坐标drawing),将文字的高度加到y坐标上,实现文字环绕。实现文本竖排的逻辑和横??排是一样的。垂直文本只是y坐标的累积。当画布高度超过画布高度时,y坐标赋0(绘图起点y坐标),x坐标加上一个文字的高度,从而实现垂直和文字环绕。部分代码片段/***Canvas绘图文本*@param{CanvasRenderingContext2D对象}context*@param{绘图内容}text*@param{起点x坐标系}x*@param{起点y坐标系}y*/drawTextVertical(context,text,x,y){让startX=x,startY=y;//记录的起始位置,用于文本换行赋值letspaceCount=0;让arrText=text.trim().split('');让formatText=text.replace(/\//g,'').split('');//移除单斜杠letalign=context.textAlign;让baseline=context.textBaseline;context.textAlign='中心';context.textBaseline='中间';context.font='Pacifico'//开始逐字绘制arrText.forEach(function(letter,index){//判断下一个字符的纵坐标位置//判断是否旋转letcode=letter.charCodeAt(0);//计算文字间距letletterWidth=22*2.3;if(code<=256){context.translate(x,y);//英文字符,旋转90°context.rotate(90*Math.PI/180);context.translate(-x,-y);}if(code!==47)context.fillText(letter,x,y);//旋转坐标系恢复到初始状态context.setTransform(1,0,0,1,0,0);//单斜杠换行或者长度超过8这里需要过滤第9个字符为换行符的情况if((code===47&&!spaceCount)||(!spaceCount&&index&&index%7===0)){//单斜杠/代表换行符charCode=47spaceCount+=1;y=开始Y;x=指数?(startX+字母宽度):x;开始X=x;}elseif(code!==47){//如果是空格,减少字间距if(code!==32){y=y+letterWidth;}else{y=y+字母宽度/2}}});//恢复水平和垂直对齐context.textAlign=align;context.textBaseline=基线;}canvas设置背景颜色Canvas在生成图片时可以指定图片格式(jpg、jpeg、png等),但只能生成位图(放大会变形)。如果想提高canvas生成图片的质量,可以引入hidpi-canvas-polyfill插件。具体使用方法可以参考这篇文章解决canvas产生的模糊图像。canvas生成的图片背景默认是透明的。如果要单独设置背景色,可以使用ctx.fillStyle来填充,但是如果设置了文字颜色,文字颜色会覆盖背景色,因为设置文字颜色也是用到了ctx.fillStyle。那么,这种情况可以通过以下方法解决:1.使用canvas.getImageData复制画布上的像素数据2.循环遍历复制的每个像素点,然后为每个像素设置rgb值3.设置流数据通过putImageData把它放回画布上。让imageData=ctx.getImageData(0,0,宽度,高度);for(leti=0;i
