H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

栏目: Html5 · 发布时间: 5年前

内容简介:最近遇到一个业务需求,在小程序端定制预览功能,并在预览的图片中使用指定的外部字体。将预览的图片上传OSS,后端生成PDF,在管理系统中下载。但是…………,经过实践发现,小程序尽管做了分包处理,依旧不能在本地存放字体包,把字体放OSS上返回,但是出现跨域,尽管配置了允许跨域,依旧不行。而且!!!小程序的

H5 canvas生成图片并上传文件转成PDF下载

最近遇到一个业务需求,在小程序端定制预览功能,并在预览的图片中使用指定的外部字体。将预览的图片上传OSS,后端生成PDF,在管理系统中下载。

但是…………,经过实践发现,小程序尽管做了分包处理,依旧不能在本地存放字体包,把字体放OSS上返回,但是出现跨域,尽管配置了允许跨域,依旧不行。而且!!!小程序的 canvas API 没法设置字体,没有h5中canvas中的 context.font = '字体名称' 方法。最终决定曲线救国,放弃小程序端的预览生成canvas功能,将canvas引入字体,生成图片等操作放在管理系统中,采用原生 canvas 来实现。

H5 canvas生成图片并上传文件转成PDF下载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;//垂直方向内内容从上到下,水平方向从左到右

具体效果如图:

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

但是这个对于浏览器也存在一定兼容性问题,使用的时候需要注意。

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

  • 使用 宽度 控制换行:(不存在兼容性,推荐方式)

设置每行的宽度为一个字大小,利用文本超出默认换行的特性,或者设置超出强制换行,实现文本竖排。

  • 利用 br 标签实现或者每个文字存放一个标签实现换行:(很死板的写法,比较low,不推荐)

给每个文字后添加 br 标签,或者每个文字放一个标签,这样写灵活性不高,非常不推荐!

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

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;
  }

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

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);

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

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以下),有可能导致文件上传失败,我之前踩过此坑。

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版

将图片生成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: '下载出错,请返回'
          });
        }
      })

H5 canvas生成图片并上传文件转成PDF下载canvas文字排版


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

More Effective C++(中文版)

More Effective C++(中文版)

梅耶(Scott Meyers) / 侯捷 / 电子工业出版社 / 2011-1-1 / 59.00元

《More Effective C++:35个改善编程与设计的有效方法(中文版)》是梅耶尔大师Effective三部曲之一。继Effective C++之后,Scott Meyers于1996推出这本《More Effective C++(35个改善编程与设计的有效方法)》“续集”。条款变得比较少,页数倒是多了一些,原因是这次选材比“一集”更高阶,尤其是第5章。Meyers将此章命名为技术(tec......一起来看看 《More Effective C++(中文版)》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具