内容简介:最近遇到一个业务需求,在小程序端定制预览功能,并在预览的图片中使用指定的外部字体。将预览的图片上传OSS,后端生成PDF,在管理系统中下载。但是…………,经过实践发现,小程序尽管做了分包处理,依旧不能在本地存放字体包,把字体放OSS上返回,但是出现跨域,尽管配置了允许跨域,依旧不行。而且!!!小程序的
H5 canvas生成图片并上传文件转成PDF下载
最近遇到一个业务需求,在小程序端定制预览功能,并在预览的图片中使用指定的外部字体。将预览的图片上传OSS,后端生成PDF,在管理系统中下载。
但是…………,经过实践发现,小程序尽管做了分包处理,依旧不能在本地存放字体包,把字体放OSS上返回,但是出现跨域,尽管配置了允许跨域,依旧不行。而且!!!小程序的 canvas API 没法设置字体,没有h5中canvas中的 context.font = '字体名称' 方法。最终决定曲线救国,放弃小程序端的预览生成canvas功能,将canvas引入字体,生成图片等操作放在管理系统中,采用原生 canvas 来实现。
技术要点
- canvas文字排版
- canvas设置指定背景颜色
- canvas引入外部字体
- canvas绘制文字图片
- 将canvas生成的base64图片转成file上传(这里根据后端协商,此处后端要求)
- 将图片生成PDF,并点击批量下载
实现步骤
canvas文字排版
在一般 HTML 容器中,如果要实现文字的排版很容易。比如:
实现 文本超出自动换行 ,默认文本超出容器宽度就会自动换行,也可以使用 word-wrap:break-word 实现强制换行。
实现 文字竖排 ,有几种方式:
- 给文本容器设置
writing-mode样式:(存在兼容性问题)
writing-mode:vertical-rl;//垂直方向自右而左的书写方式。即 top-bottom-right-left 或者 writing-mode:vertical-lr;//垂直方向内内容从上到下,水平方向从左到右
具体效果如图:
但是这个对于浏览器也存在一定兼容性问题,使用的时候需要注意。
- 使用
宽度控制换行:(不存在兼容性,推荐方式)
设置每行的宽度为一个字大小,利用文本超出默认换行的特性,或者设置超出强制换行,实现文本竖排。
- 利用
br标签实现或者每个文字存放一个标签实现换行:(很死板的写法,比较low,不推荐)
给每个文字后添加 br 标签,或者每个文字放一个标签,这样写灵活性不高,非常不推荐!
在 canvas 中实现文字排版
- 实现文字横排
canvas 中,如果文本超出 canvas 大小,并不会自动换行,会直接在超出的后面继续绘制成一排。
canvas 中也没有直接可以设置换行的api,那该怎么实现换行呢?
可以通过 js 控制,通过计算当前绘制文字的 x 坐标,如果x坐标大于 canvas 的宽度,将 x 坐标赋值为 0 (绘制的起始点x坐标), y 坐标累加一个文字的高度,从而实现文本换行。
- 实现文字竖排
竖排的逻辑和横排是一样的。文字竖排只是 y 坐标累加,趟超过 canvas 的高度时,将 y 坐标赋值为 0 (绘制的起始点y坐标), x 坐标累加一个文字的高度,从而实现竖排且文本换行。
部分代码片段
/**
* canvas绘制文字
* @param {CanvasRenderingContext2D对象} context
* @param {绘制内容} text
* @param {起始点x坐标制} x
* @param {起始点y坐标制} y
*/
drawTextVertical(context, text, x, y) {
let startX = x,
startY = y; //记录开始的位置,用于文字换行赋值
let spaceCount = 0;
let arrText = text.trim().split('');
let formatText = text.replace(/\//g, '').split(''); // 去掉单斜杠
let align = context.textAlign;
let baseline = context.textBaseline;
context.textAlign = 'center';
context.textBaseline = 'middle';
context.font = 'Pacifico'
// 开始逐字绘制
arrText.forEach(function (letter, index) {
// 确定下一个字符的纵坐标位置
// 是否需要旋转判断
let code = letter.charCodeAt(0);
// 计算文字间距
let letterWidth = 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=47
spaceCount += 1;
y = startY;
x = index ? (startX + letterWidth) : x;
startX = x;
} else if (code !== 47) {
// 如果是空格 减少字间距
if (code !== 32) {
y = y + letterWidth;
} else {
y = y + letterWidth / 2
}
}
});
// 水平垂直对齐方式还原
context.textAlign = align;
context.textBaseline = baseline;
}
canvas设置背景颜色
canvas 生成图片的时候可以指定图片格式(jpg,jpeg,png等),但是只能生成 位图 (放大会失真)。如果想提高 canvas 生成图片的质量,可以引入 hidpi-canvas-polyfill 插件,具体使用可以参考这篇文章 解决canvas生成图片模糊 。
canvas 生成图片的背景默认是透明的,如果想单独设置背景颜色,可以使用 ctx.fillStyle 进行填充,但是设置文字颜色,则文字颜色会覆盖背景颜色,因为设置文字颜色也是使用 ctx.fillStyle 。那么,这种情况可以使用一下办法解决:
1、使用 canvas.getImageData 复制画布上的像素数据
2.循环遍历复制的每个像素点,然后给每个像素设置 rgb 值
3.将设置好的流数据通过 putImageData 放回画布上。
let imageData = ctx.getImageData(0, 0, width, height);
for (let i = 0; i < imageData.data.length; i += 4) {
// 当该像素是透明的,则设置成白色
if (imageData.data[i + 3] == 0) {
imageData.data[i] = 255;
imageData.data[i + 1] = 255;
imageData.data[i + 2] = 255;
imageData.data[i + 3] = 255;
}
}
ctx.putImageData(imageData, 0, 0);
但是要注意背景和文字的绘制顺序, 必须先绘制背景,再绘制文字 ,如果顺序颠倒,则文字会出现很明显的锯齿状,有点模糊,这就和定位中 z-index 原理类似。
canvas引入外部字体
1.首先引入字体库,为了节省本地空间,可以从服务端引入,但是需要 注意跨域问题 ,
也可以将字体库放本地,直接相对路径引入。
// 从服务端引入
@font-face {
font-family: "FZCUJINLJW";
src: url('https://www.xxxx.com/FZCUJINLJW.TTF') ;
}
// 本地引入
@font-face {
font-family: "FZCUJINLJW";
src: url('../../assets/FZCUJINLJW.TTF') ;
}
2.通过 CanvasRenderingContext2D 对象设置字体,字号等
ctx.font = '24px FZCUJINLJW'; ctx.fillStyle = '#db9a00';//填充颜色
canvas绘制文字图片
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');//拿到一个CanvasRenderingContext2D对象
ctx.beginPath();// 开始绘制文字
ctx.font = `${FONT_SIZE}px FZCUJINLJW`;
ctx.fillStyle = '#db9a00';//填充颜色
ctx.fillText('绘制的内容', /*绘制的x坐标*/, /*绘制的y坐标*/);
let imgBase64 = canvas.toDataURL('image/png', 1);
ctx.closePath();
ctx.save();// 保存当前画布内容
//如果需要在画布上循环绘制多次,需要手动清除画布上已经保存的内容,如果不清除,则画布内容会叠加。
ctx.clearRect(0, 0, canvasObj.width, canvasObj.height);
canvas生成图片并上传服务端
通过 ctx.toDataURL 可以获取到画布内容的 base64编码
let imgBase64 = canvas.toDataURL('image/png', 1);
如果服务端支持使用 base64 上传,则不用处理,此处因为后端需要file文件类型,所以需要将 base64 转成 file对象 ,代码如下:
let file = dataURLtoFile(imgBase64, 'jpg'); // 将base转为file对象
function dataURLtoFile(urlData, fileName) {
var bytes = window.atob(urlData.split(',')[1]); //去掉url的头,并转换为byte
var mime = urlData.split(',')[0].match(/:(.*?);/)[1];
//处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new File([ab], fileName, { type: mime });
}
转成 file对象 后,通过 FormData 格式上传
let formdata = new FormData();
formdata.append('multipartList', file);
ajax.post(url,data:formdata).then()
此处需要注意,当 canvas 生成的图片比较小时(比如5kb以下),有可能导致文件上传失败,我之前踩过此坑。
将图片生成PDF,并点击批量下载
此处是和后端商量,将 canvas 生成的图片上传服务端,并返回图片的 OSS 地址,再将此地址作为参数传给后端,获取到 PDF 的下载链接,前端通过 window.open(url) 的方式实现文件下载。
let uploadUrl = window.interfercesPrefix + '/admin/goods/tbgoods/uploadImages';
let downLoadUrl = '/app/goods/tbgoods/downLoadPdf';
// 上传图片
ajaxUploderImg({ url: uploadUrl, data: formdata }).then(res => {
// 将图片作为参数获取PDF下载地址
this.props.dispatch(downLoadPdf({ url: downLoadUrl, imgUrl: res.data }));
}).catch(err => {
if (err) {
notification['error']({
message: err.message,
description:
'图片绘制出错,请重试!',
});
} else {
notification['error']({
message: '下载出错,请返回'
});
}
})
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Bootstrap学习之三:使用排版
- R语言图表排版之一页多图
- 以 Markdown 撰写文稿,以 LaTeX 排版
- Android9编程七:ConstraintLayout 排版
- 中英文排版规范化 API
- [译]《Smashing》: 用 CSS 形状打造高级排版
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ant Colony Optimization
Marco Dorigo、Thomas Stützle / A Bradford Book / 2004-6-4 / USD 45.00
The complex social behaviors of ants have been much studied by science, and computer scientists are now finding that these behavior patterns can provide models for solving difficult combinatorial opti......一起来看看 《Ant Colony Optimization》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
UNIX 时间戳转换
UNIX 时间戳转换