专栏首页全栈修仙之路Node.js 小打小闹之图片合成

Node.js 小打小闹之图片合成

前阵子公司的产品经理找我谈个需求,希望能为每个用户生成专属的资讯分享图片及让开通专栏的用户能够生成专属的文章分享图片。这两天刚好有空,就抽空预研了 “生成专属的资讯分享图片” 这个功能。

进入正题前,我们先来看一下最终实现的效果图:

需求分析

接下来我们来简单的介绍一下 “生成专属的资讯分享图片” 这个功能需求:

  1. 图片中有个区域能够显示分享用户的头像和昵称;
  2. 图片中需要显示用户的一些数据信息;
  3. 图片底部需要展示 App 的二维码信息。

针对这个需求,我们主要有两种方案:客户端生成与服务端生成。既然文章标题已定,我们肯定选择 “服务端生成” 的方案。确定完方案后,我并没有马上动手开始开发,而是先在网上找一些相关的文章,因为觉得这个需求应该挺常见的。果然通过一番检索,找到了用程序生成一张在简书的专属分享图片这篇文章。文章作者对功能做了详细的分析,然后利用 Python 强大的图片处理库 Pillow 进行功能实现。建议有兴趣的同学,直接阅读原文。

虽然 Python 勉强入门,作者写的代码也基本能看懂,但作为一个喜欢折腾的小前端,怎能不使用我们的 Node.js 来折腾一下呢?说做就做,马上到 npm 上挑选 Node.js 的图片处理库。经过一番筛选,最终选中了 sharp,这是为什么?当然是喜欢它的名字咯(嘿嘿,其实是看中它的高性能)。

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. Uses the libvips library. http://sharp.pixelplumbing.com/

再次感谢 用程序生成一张在简书的专属分享图片 该文章的作者,他把项目源码和资源都放到了 Github - jianshu_share 上。因为只是技术预研,我就直接使用该项目的图片资源,接下来我们来介绍一下具体实现。

实现步骤

  • 裁剪头像(方形 -> 圆形):通过查看 sharp 项目的说明文档,我发现了裁剪头像的方案,具体实现如下:
// 创建圆形SVG,用于实现头像裁剪
const roundedCorners = new Buffer(
  '<svg><circle r="90" cx="90" cy="90"/></svg>'
);

/**
 * 生成圆形的头像
 * @param {*} avatarPath 头像路径
 */
function genCircleAvatar(avatarPath) {
  return sharp(avatarPath)
    .resize(180, 180)
    .overlayWith(roundedCorners, { cutout: true })
    .png()
    .toBuffer({
      resolveWithObject: true
    });
}
  • 叠加背景图、头像和二维码图层:这个功能与 PS 的图层叠加类似,其实就是把裁剪过的头像和二维码,贴到背景图的指定位置。要实现这个功能也是需要使用 sharp 提供的 overlayWith 方法。
// 组合多个图层:图片+文字图层
  return buffers
    .reduce((input, overlay, index) => {
      return input.then(result => {
        console.dir(overlay.info);
        return sharp(result.data)
          .overlayWith(overlay.data, overlayOptions[index])
          .toBuffer({ resolveWithObject: true });
      });
    }, backgroudBuffer)
    .then((data) => {
      return sharp(data.data).toFile(outFilePath);
    }).catch(error => {
      throw new Error('Generate Share Image Failed.');
    });
  • 根据用户信息创建文本:在实现这个功能的时候,遇到了一个问题。因为官方的 API 没有提供文件创建图片的方法,最终参考了 sharp 项目中 How to dynamically write text to image? 这个 issue 中提供的方案,解决了这个问题。 即利用 text-to-svg 这个库,先把文本转换成 SVG,然后在利用 overlayWith 方法进行图层合并。
// 加载字体文件
const textToSVG = TextToSVG.loadSync(path.join(__dirname, "./simhei.ttf"));

// 设置SVG文本元素相关参数
const attributes = { fill: "white" };
const svgOptions = {
  x: 0,
  y: 0,
  fontSize: 32,
  anchor: "top",
  attributes: attributes
};

/**
 * 使用文本生成SVG
 * @param {*} text 
 * @param {*} options 
 */
function textToSVGFn(text, options = svgOptions) {
  return textToSVG.getSVG(text, options);
}
  • 叠加所有图层,生成最终的图片:实现最后一步的时候,也是遇到了一个大坑,官方没有提供对应的方法。幸运的是,最终也是通过项目中 Easier way to composite 3+ layers 这个 issue 提供的方案,完美解决了问题。

经过大半天地折腾,终于借助 Node.js 的 sharp 这个图片处理库,基本实现了上述的功能。由于源码过长,我直接放在 Gist 上,有兴趣的小伙伴可以查看 gen-share-image.js 完整源码。

总结

本文主要介绍了如何利用 Node.js 的 sharp 图片处理库,生成专属的分享图片。源码中有很多细节需要处理,如动态获取头像、根据参数动态生成文本信息、异常处理及基于 Koa、Egg.js 或 Express 框架,创建对应的 API 服务等。 gen-share-image.js 只是介绍了完整的思路和实现方式,实际开发的时候,请根据具体需求进行调整。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Sequelize 系列教程之多对多模型关系

    Sequelize 是一个基于 Promise 的 Node.js ORM,目前支持 Postgres、MySQL、SQLite 和 Microsoft SQL...

    阿宝哥
  • customElements 实战之 Lite-embed

    Lite-embed 的灵感来源于 paulirish 大神的 lite-youtube-embed 项目:

    阿宝哥
  • TypeScript typeof 操作符

    在 TypeScript 中,typeof 操作符可以用来获取一个变量或对象的类型。

    阿宝哥
  • 扩展正则表达式

    逐梦的青春
  • 马斯克称特斯拉AutoPilot方案宛如“超人”,首要任务是“不撞车”;网友:???

    这不,一位网友po出了一段特斯拉夜间行车的视频——AutoPilot成功让车辆避免了一起撞猪事故:

    量子位
  • 机器学习都能做什么?这里有来自8家公司的实战经验

    最近,300多名数据科学家、工程师、研究员在纽约曼哈顿参加了一场关于机器学习的大会,名叫Machine Learning @Scale。 会上,来自Google...

    量子位
  • ElasticSearch-hard插件及IK分词器安装

    返回的都是JSON格式的。不方便查看。如果有页面可以查看就更好了。有没有呢?当然有了:elasticSeard-head这个插件就可以实现这个功能。

    凯哥Java
  • 静默安装Oracle Database 18c

    系统环境:Oracle Linux 7(OL7) 一、首先设置主机名和ip,修改/etc/hosts (很简单,不赘述) 二、Oracle安装先决条件 执行自...

    孙杰
  • 002.Ceph安装部署

    Ceph Monitors之间默认使用6789端口通信,OSD之间默认用6800:7300范围内的端口通信。Ceph OSD能利用多个网络连接进行与客户端、mo...

    木二
  • iOS lable多行取每行字符串

    最近项目需要从lable的中去每行添加换行符,在网上找了一些资料,总结了一下怎么处理这个问题。

    菜菜不吃蔡

扫码关注云+社区

领取腾讯云代金券