Node.js 小打小鬧之圖片合成
前陣子公司的產品經理找我談個需求,希望能為每個使用者生成專屬的資訊分享圖片及讓開通專欄的使用者能夠生成專屬的文章分享圖片。這兩天剛好有空,就抽空預研了 “生成專屬的資訊分享圖片” 這個功能。
進入正題前,我們先來看一下最終實現的效果圖:
需求分析
接下來我們來簡單的介紹一下 “生成專屬的資訊分享圖片” 這個功能需求:
- 圖片中有個區域能夠顯示分享使用者的頭像和暱稱;
- 圖片中需要顯示使用者的一些資料資訊;
- 圖片底部需要展示 App 的二維碼資訊。
針對這個需求,我們主要有兩種方案:客戶端生成與服務端生成。既然文章標題已定,我們肯定選擇 “服務端生成” 的方案。確定完方案後,我並沒有馬上動手開始開發,而是先在網上找一些相關的文章,因為覺得這個需求應該挺常見的。果然通過一番檢索,找到了 ofollow,noindex">用程式生成一張在簡書的專屬分享圖片 這篇文章。文章作者對功能做了詳細的分析,然後利用 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 只是介紹了完整的思路和實現方式,實際開發的時候,請根據具體需求進行調整。