概述

最近项目用到了canvas转图片,但是由于canvas对文字排版的支持非常弱,一般我们在canvas上画不同排版的文字(比如竖排文字)都是利用js计算横纵坐标,然后一个字一个字地画出来,今天无意中看到一个使用svg的方法,记录下来,供以后开发时参考,相信对其他人也有用。

参考资料:

SVG 简介与截图等应用

svg和canvas介绍

svg和canvas都支持图形渲染,它们各有侧重:

  • svg:更适用于高保真文档,静态图像,可交互图表图形,2D游戏等。
  • canvas:更适用于屏幕截图,视频操作,很多对象的复杂场景,web广告等。

foreignObject元素

foreignObject元素是个非常强大的元素,它可以在其中使用具有其它XML命名空间的XML元素,简单来说,就是我们可以利用foreignObject元素把html“画在”svg里面!

示例如下。其中xmlns表示XML命名空间,标记它是一个svg或者一个xhtml。注意:svg和xhtml都属于XML。

<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="120" height="50">
<body xmlns="http://www.w3.org/1999/xhtml">
<p>文字。</p>
</body>
</foreignObject>
</svg>

另外,所有的svg都必须加上这个svg的命名空间标识,否则它就会以XML文档树的形式渲染。示例如下:

//渲染出来的是数字
<svg width="12" height="12" viewBox="0 0 12 12"><path d="M10.263 10.874L6 6.61l-4.264 4.264a.431.431 0 0 1-.61-.61L5.39 6.001 1.128 1.736a.431.431 0 0 1 .61-.61L6 5.391l4.264-4.263a.431.431 0 0 1 .61.61L6.61 6l4.262 4.264a.43.43 0 1 1-.61.609z" stroke="#979797" fill="#999"/></svg> //渲染出来的是图形
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="M10.263 10.874L6 6.61l-4.264 4.264a.431.431 0 0 1-.61-.61L5.39 6.001 1.128 1.736a.431.431 0 0 1 .61-.61L6 5.391l4.264-4.263a.431.431 0 0 1 .61.61L6.61 6l4.262 4.264a.43.43 0 1 1-.61.609z" stroke="#979797" fill="#999"/></svg>

canvas中文本换行

由于我们可以利用foreignObject把html转化为SVG,然后我们可以把svg画在canvas里面,最后用canvas把它转化为图形

示例如下:

//首先初始化canvas
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d'); //获取文字的长度
context.font = "20px 微软雅黑";
var textLength = context.measureText(text).width; //画一个svg图片
var img = new Image(); //对文字长度做判断,一行的文字字要大些,二行的文字字要小些
if(textLength <= 135) {
img.src = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="270" height="45"><body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;color:#e7793f;text-align:center;font-size:40px;line-height:45px;font:微软雅黑;">'+ text +'</body></foreignObject></svg>';
} else {
img.src = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="270" height="60"><body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;color:#e7793f;text-align:left;font-size:25px;line-height:30px;font:微软雅黑;overflow: hidden;display: -webkit-box;text-overflow: ellipsis;--webkit-box-orient: vertical;--webkit-line-clamp: 2;word-break: break-all;">'+ text +'</body></foreignObject></svg>';
} //把图片绘制上去,并利用canvas生成图片
img.onload = function() {
context.drawImage(img, 72, 1080);
document.querySelector('#ticket').src = canvas.toDataURL('image/png');
}

canvas文字竖排

理论上有三种方法来解决:

1.利用js逐个字符逐行绘制,但是这样对英文要做特殊处理,不能单个字符绘制,而应该旋转90度。相关参考代码如下:

fillTextVertical: function(context, text, x, y) {
var canvas = context.canvas; var arrText = text.split('');
var arrWidth = arrText.map(function (letter) {
return context.measureText(letter).width;
}); var align = context.textAlign;
var baseline = context.textBaseline; //emoji的位置
var emoji_arr = fn.findEmoji(text); //emoji第一位的缓存
var emoji_first = ''; if (align == 'left') {
x = x + Math.max.apply(null, arrWidth) / 2;
} else if (align == 'right') {
x = x - Math.max.apply(null, arrWidth) / 2;
}
if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') {
y = y - arrWidth[0] / 2;
} else if (baseline == 'top' || baseline == 'hanging') {
y = y + arrWidth[0] / 2;
} context.textAlign = 'center';
context.textBaseline = 'middle'; // 开始逐字绘制
arrText.forEach(function (letter, index) {
// 确定下一个字符的纵坐标位置
var letterWidth = arrWidth[index];
// 是否需要旋转判断
var code = letter.charCodeAt(0); //是否是emoji的第二位
if(emoji_arr.indexOf(index) != -1) {
emoji_first = letter;
return;
}
//是否是emoji
if(emoji_arr.indexOf(index - 1) != -1) {
letter = emoji_first + letter;
emoji_first = '';
} else {
if (code <= 256) {
context.translate(x, y);
// 英文字符,旋转90°
context.rotate(90 * Math.PI / 180);
context.translate(-x, -y);
} else if (index > 0 && text.charCodeAt(index - 1) < 256) {
// y修正
y = y + arrWidth[index - 1] / 2;
}
} context.fillText(letter, x, y);
// 旋转坐标系还原成初始态
context.setTransform(1, 0, 0, 1, 0, 0);
// 确定下一个字符的纵坐标位置
var letterWidth = arrWidth[index];
y = y + letterWidth;
});
// 水平垂直对齐方式还原
context.textAlign = align;
context.textBaseline = baseline;
},

上面方法的缺点是,对emoji符号使用正则匹配的,但仍然会有部分emoji符号匹配不到导致乱码。

2.使用foreignObject绘制。但是有一些bug,就是在安卓机上emoji表情会错位,而且在ios上会被莫名截断。

3.使用html2canvas库。还是有一个bug,就是对英文的竖排支持不是很理想,英文会横排显示。

总之上面的方法各有各的优缺点,完美的竖排兼容emoji的实现是不可能的,这辈子都不可能的!0.0

canvas转图片中的文字自动换行的更多相关文章

  1. canvas文字自动换行、圆角矩形画法、生成图片手机长按保存、方形图片变圆形

    canvas的文字自动换行函数封装 // str:要绘制的字符串 // canvas:canvas对象 // initX:绘制字符串起始x坐标 // initY:绘制字符串起始y坐标 // lineH ...

  2. Azure 认知服务 (5) 计算机视觉API - 使用C#代码实现读取图片中的文字(OCR)功能

    <Windows Azure Platform 系列文章目录> 在笔者之前的文章:Azure 认知服务 (4) 计算机视觉API - 读取图片中的文字 (OCR) 介绍了使用用户界面,在海 ...

  3. Azure 认知服务 (4) 计算机视觉API - 读取图片中的文字 (OCR)

    <Windows Azure Platform 系列文章目录> 微软Azure认知服务的计算机视觉API,还提供读取图片中的文字功能 在海外的Windows Azure认知服务的读取图片功 ...

  4. 使用Python进行OCR -- 识别图片中的文字

    工具 Tesseract pytesseract tesserocr 朋友需要一个工具,将图片中的文字提取出来.我帮他在网上找了一些OCR的应用,都不好用.所以准备自己研究,写一个Web APP供他使 ...

  5. Mac上如何把图片中的文字转换成word/pdf文字

    如何把图片文字转换成word文字? - 知乎 https://www.zhihu.com/question/25488536 在 OneNote for Mac 中插入的圖片複製文字 - OneNot ...

  6. 使用echart的雷达图的时候,如果文字越界的解决办法记录,标签文字自动换行

    使用echart的雷达图的时候,如果文字越界的解决办法记录,标签文字自动换行 前几天项目中有一个图表的是用echart生成的,遇到一个问题,就是在手机端显示的售时候,如果文字太长就会超出div,之前的 ...

  7. 电脑端的全能扫描王:图片转文字识别、识别pdf、图片中的文字,图片提取txt

    手机中有全能扫描王,但PC端没有.所以需要另外找. 发现微软的oneNode有提供类似的功能. 第一步.下载Microsoft OneNode http://www.onenote.com/downl ...

  8. java从图片中识别文字

    package com.dream.common; import java.awt.image.BufferedImage; import java.io.File; import java.io.I ...

  9. HTML DIV中文字自动换行 , 顶部对齐

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta ht ...

随机推荐

  1. PowerDesigner反向生成PDM和name与注释互换

    Option Explicit ValidationMode = True InteractiveMode = im_Batch Dim mdl 'the current model 'get the ...

  2. Oracle启动和停止

    概述 只有具备sysdba和sysoper系统特权的用户才能启动和关闭数据库. 在启动数据库之前应该启动监听程序,否则就不能利用命令方式来管理数据库,包括启动和关闭数据库. 虽然数据库正常运行,但如果 ...

  3. JSP 页面跳转中的参数传递

    1. 从一个 JSP 页面跳转到另一个 JSP 页面时的参数传递 1)使用 request 内置对象获取客户端提交的信息 2)使用 session 对象获取客户端提交的信息 3)利用隐藏域传递数据 4 ...

  4. Java-常用工具方法

    一 Json转换 1 输出组装好的json ObjectMapper mapper = new ObjectMapper(); try { String requiredJson = mapper.w ...

  5. Mac系统下 解决ThinkPHP生成目录,无法保存问题

    Mac环境下我们建立目录的时候往往要增加目录的时候要修改权限,输入密码,大大的降低了效率. 解决办法: 1.找到你的目录站点 终端打开打 2.终端输入find file -exec sudo chmo ...

  6. Javascript中表达式和语句的区别

    一.表达式:一个表达式会产生一个值,它可以放在任何需要一个值的地方,比如,作为一个函数调用的参数. 以下例子就是表达式: a=35: b=1+a; a=function (){return 6}: b ...

  7. 配置json-server

    1.全局安装json-server[可能需要管理员权限] npm i -g json-server 2.创建文件夹jsonerver,初始化package.json文件npm init 3.局部安装j ...

  8. iBtais 多重嵌套循环

    iBatis支持集合循环, 但是如何做到双重循环, 请见下例子 例子描述: 需要去三张结构相同的表中获取信息, 需要将信息拼合去重后返回 入参数据类型: Map<String,Object> ...

  9. UGUI小技巧之Text随文本内容自动变化大小

    看了网上很多帖子,都是说在 Text 上面加上 Content Size Fitter 组件,并将对应的轴向改成 Preferred size 就可以实现 Text 大小随着文本内容自适应,如下图: ...

  10. eclipse启动时要求高版本jdk的问题

    在eclipse.ini文件首行添加 -vm C:\Program Files\Java\jdk1.8\jdk1.8.0_131\bin https://blog.csdn.net/wanlin77/ ...