|  | /** | 
        
          |  | * 说明信息 | 
        
          |  | * 1.目录结构: | 
        
          |  | * ├── avatar.jpg | 
        
          |  | * ├── qrcode.jpg | 
        
          |  | * ├── share-image.js | 
        
          |  | * ├── simhei.ttf | 
        
          |  | * └── template.jpg | 
        
          |  | * 2.备注:若出现资源路径异常时,则使用path.join(__dirname, "./simhei.ttf")的方式进行资源加载 | 
        
          |  | * 3.示例: | 
        
          |  | * share.genShareImage({ | 
        
          |  | *   backgroudPath: "template.jpg", | 
        
          |  | *   avatarPath: "avatar.jpg", | 
        
          |  | *   qrcodePath: "qrcode.jpg", | 
        
          |  | *   userName: "semlinker", | 
        
          |  | *   words: 2888, | 
        
          |  | *   likes: 188, | 
        
          |  | *   outFilePath: path.join(__dirname, `public/images/${+new Date()}.png`) | 
        
          |  | * }) | 
        
          |  | */ | 
        
          |  |  | 
        
          |  | const sharp = require("sharp"); | 
        
          |  | const TextToSVG = require("text-to-svg"); | 
        
          |  | const path = require("path"); | 
        
          |  |  | 
        
          |  | // 加载字体文件 | 
        
          |  | const textToSVG = TextToSVG.loadSync(path.join(__dirname, "./simhei.ttf")); | 
        
          |  |  | 
        
          |  | // 创建圆形SVG,用于实现头像裁剪 | 
        
          |  | const roundedCorners = new Buffer( | 
        
          |  | '<svg><circle r="90" cx="90" cy="90"/></svg>' | 
        
          |  | ); | 
        
          |  |  | 
        
          |  | // 设置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); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | /** | 
        
          |  | * 图层叠加生成分享图片 | 
        
          |  | * @param {*} options | 
        
          |  | * | 
        
          |  | */ | 
        
          |  | async function genShareImage(options) { | 
        
          |  | const { backgroudPath, avatarPath, qrcodePath, | 
        
          |  | userName, words, likes, outFilePath | 
        
          |  | } = options; | 
        
          |  |  | 
        
          |  | // 背景图片 | 
        
          |  | const backgroudBuffer = sharp(path.join(__dirname, backgroudPath)).toBuffer({ | 
        
          |  | resolveWithObject: true | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | const backgroundImageInfo = await backgroudBuffer; | 
        
          |  | // 头像图片 | 
        
          |  | const avatarBuffer = await genCircleAvatar(path.join(__dirname, avatarPath)); | 
        
          |  |  | 
        
          |  | // 二维码图片 | 
        
          |  | const qrCodeBuffer = await sharp(path.join(__dirname, qrcodePath)) | 
        
          |  | .resize(180) | 
        
          |  | .toBuffer({ | 
        
          |  | resolveWithObject: true | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | // 用户名 | 
        
          |  | const userNameSVG = textToSVGFn(userName); | 
        
          |  | // 用户数据 | 
        
          |  | const userDataSVG = textToSVGFn(`写了${words}个字   收获${likes}个赞`); | 
        
          |  | const userNameBuffer = await sharp(new Buffer(userNameSVG)).toBuffer({ | 
        
          |  | resolveWithObject: true | 
        
          |  | }); | 
        
          |  | const userDataBuffer = await sharp(new Buffer(userDataSVG)).toBuffer({ | 
        
          |  | resolveWithObject: true | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | const buffers = [avatarBuffer, qrCodeBuffer, userNameBuffer, userDataBuffer]; | 
        
          |  | // 图层叠加参数列表 | 
        
          |  | const overlayOptions = [ | 
        
          |  | { top: 150, left: 230 }, | 
        
          |  | { top: 861, left: 227 }, | 
        
          |  | { | 
        
          |  | top: 365, | 
        
          |  | left: (backgroundImageInfo.info.width - userNameBuffer.info.width) / 2 | 
        
          |  | }, | 
        
          |  | { | 
        
          |  | top: 435, | 
        
          |  | left: (backgroundImageInfo.info.width - userDataBuffer.info.width) / 2 | 
        
          |  | } | 
        
          |  | ]; | 
        
          |  |  | 
        
          |  | // 组合多个图层:图片+文字图层 | 
        
          |  | 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.'); | 
        
          |  | }); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | /** | 
        
          |  | * 生成圆形的头像 | 
        
          |  | * @param {*} avatarPath 头像路径 | 
        
          |  | */ | 
        
          |  | function genCircleAvatar(avatarPath) { | 
        
          |  | return sharp(avatarPath) | 
        
          |  | .resize(180, 180) | 
        
          |  | .overlayWith(roundedCorners, { cutout: true }) | 
        
          |  | .png() | 
        
          |  | .toBuffer({ | 
        
          |  | resolveWithObject: true | 
        
          |  | }); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | module.exports = { | 
        
          |  | genShareImage | 
        
          |  | }; | 
  
nice