Sharp 牛刀小試之生成專屬分享圖片

Sharp 牛刀小試之生成專屬分享圖片

前陣子公司的產品經理找我談個需求,但願能爲每一個用戶生成專屬的資訊分享圖片及讓開通專欄的用戶可以生成專屬的文章分享圖片。這兩天恰好有空,就抽空預研了 "生成專屬的資訊分享圖片" 這個功能。javascript

進入正題前,咱們先來看一下最終實現的效果圖:前端

clipboard.png

需求分析

接下來咱們來簡單的介紹一下 "生成專屬的資訊分享圖片" 這個功能需求:java

  1. 圖片中有個區域可以顯示分享用戶的頭像和暱稱;
  2. 圖片中須要顯示用戶的一些數據信息;
  3. 圖片底部須要展現 App 的二維碼信息。

瞭解完這個需求,我腦海中有三個可選方案:python

  1. 在 App 端使用原生提供的 API 進行圖片合成;
  2. 在服務端經過特定語言提供的圖片處理庫,進行圖片合成;
  3. 在服務端根據不一樣的用戶信息生成網頁,而後使用 HTML 轉 IMAGE 的庫;或使用 iOS 企業證書過時填坑記 文章介紹過的神器 puppeteer 提供的 API 實現全屏截圖功能。

因爲對 App 端(Android 和 iOS)都不熟,只懂了點皮毛,因此打算就直接嘗試第二種方案。肯定使用第二種方案,我並無立刻動手開始開發,而是先在網上找一些相關的文章,由於以爲這個需求應該挺常見的。果真經過一番檢索,找到了用程序生成一張在簡書的專屬分享圖片這篇文章。文章做者對功能作了詳細的分析,而後利用 Python 強大的圖片處理庫 Pillow 進行功能實現。建議有興趣的同窗,直接閱讀原文。ios

雖然 Python 勉強算入門,結合 Pillow 的 API 文檔,做者寫的代碼基本能看懂,但做爲一個喜歡折騰的小前端,怎能不使用咱們的 Node.js 來折騰一下呢?說作就作,固然立刻到 npm 上挑選 Node.js 的圖片處理庫。通過一番篩選,最終選中了 sharp,這是爲何?固然是喜歡它的名字咯(嘿嘿,實際上是看中它的高性能)。git

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 上。由於只是技術預研,我就直接使用該項目的圖片資源,接下來咱們來介紹一下具體實現。github

實現步驟

  • 裁剪頭像(方形 -> 圓形):經過查看 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 完整源碼。npm

總結

本文主要介紹瞭如何利用 Node.js 的 sharp 圖片處理庫,生成專屬的分享圖片。源碼中有不少細節須要處理,如動態獲取頭像、根據參數動態生成文本信息、異常處理及基於 Koa、Egg.js 或 Express 框架,建立對應的 API 服務等。 gen-share-image.js 源碼只是介紹了完整的思路和實現方式,實際開發的時候,在根據具體需求進行調整。雖然目前是已經基於 Koa 開發了一個簡單API服務,但還有一些流程須要優化,有興趣瞭解具體細節或有更好方案的小夥伴能夠給我留言哈框架

相關文章
相關標籤/搜索