關聯內部鏈接:
- [[速通 - unjs 家族的 Web 框架 Nitro]]
- [[速通 - 騰訊雲雲函數 scf]]
- [[編寫一個小工具方便快速為文章添加 Banner]]
- [[速通 - 動態 SVG 圖片生成方案 Satori]]
背景#
現在比較習慣在文章頂部增加一張包含當前標題的圖片,在不同平台會默認第一張圖片有用,比如認為這是文章 Banner、Cover、Hero Image 等說法。
之前在 [[編寫一個小工具方便快速為文章添加 Banner]] 文章中就提到了我寫了一個網站,可以在頁面上操作生成 banner。
那有沒有一種辦法直接寫一個服務,當我訪問 xx.com/cover?title=xxx
時候自動返回一張圖片,這樣的話,我就不需要打開頁面去創建了。後續我只需要查看日誌就知道有多少人訪問過啦!
而且不同於瀏覽器,在 node.js 裡渲染圖片要更複雜一點,不可以像瀏覽器那樣把計算、渲染的能力丟給瀏覽器了,最直接的就是 html+css 生成截圖不可以了,因為 node 環境裡沒有 css 渲染的能力,得靠 puppeteer 那一套,得使用 canvas/svg 等方案來畫。
Canvas 的跨平台兼容就顯現出來了。但 Canvas 沒接觸過不太會,得現學。後來發現 svg 也非常簡單,而且返回的是字符串,體積更小。
魚和熊掌不可兼得啊。
需求設計#
首先這是一個 node 服務,我們攜帶參數請求接口,返回的是一張圖片。這就需要我們部署後端。
相通的請求參數應該出現一致的結果,主要是背景圖,不要一刷新頁面裡的圖片變了,產生不必要的誤會。
後續可以增加不同的模板,展望可以做成服務對外提供。
參數我們希望:
- width 寬度
- height 可選,自動高度
- 比例 4:3 16:9 21:9 2:1 1:1 等
- title 文字內容
- source 角標,比如
@ijust.cc
- 指定色相 0-360
- 默認開啟漸變 linear-gradient
這樣基本就和網頁版 https://ijust.cc/tools/canvas 一致了。
技術設計#
交付托管#
node 服務托管,顯然是可以使用 serverless,我們可以選擇國內的雲廠商服務,也可以使用 cloudflare/vercel 提供的服務。考慮服務為了正常需要遷移,可以選擇 serverless 框架,之前提到過的 [[速通 - unjs 家族的 Web 框架 Nitro]] 就可以派上用場了。Nitro 可以很容易構建出不同的產物。
雲廠商這裡我們使用騰訊雲 Serverless 函數,後面補充 Cloudflare Workers。
主題色持久化#
相通的內容產生相通的結果,設計一個方案,根據 title 內容算出一個數字,表達主色調。問了 GPT 給了幾個方案,比如每個字符串去算 charCodeAt,得到的數字進行處理。
function stringToDegrees(inputString) {
let hash = 0;
for (let i = 0; i < inputString.length; i++) {
let char = inputString.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0; // 將哈希值轉換為32位整數
}
return Math.abs(hash % 360);
}
stringToDegrees('中文') // 194
使用 Satori 製作動態 SVG#
畫布因為考慮 Canvas,很多東西就不能復用了,經過實踐發現 Vercel 的 Satori最近比較火熱,在 [[12 - 速通 syntax.fm 659 OG Image]] 這一期其實也提到過。寫 demo 成功了,以後能經常用得上。
這麼一張 SVG 圖,才 4.5k 大小,要是 jpg 和 png 都不敢想。至少大 10 倍、20 倍。
技術實現#
這裡我們準備一個新倉庫用來解釋這一切,後續也可以作為分享內容。
代碼在這裡 https://github.com/Otto-J/serverless-dynamic-banner
我先去寫代碼了,寫完代碼回來補充。
經過一些時間後,終於寫完了,serverless 服務也上線可用了。大量吐槽來了,氣人😡。
踩坑經驗#
之前講到本次技術選擇是:nitro + 騰訊雲 serverless,目標是上線 serverless 服務,最終我們手裡有一個網址,一打開是一張圖片。
Nitro 文檔稀爛#
我選擇 nitro 有兩個原因:
- 不用我學習新語法,比如 next.js 的那些概念,輕鬆上手,比 express/koa 相比不是玩具,大量內置了 ts、路由、中間件、常用方法,更快更容易寫出真實業務代碼
- Nitro 是 nuxt 的組成部分,學會了 nitro 一魚兩吃,寫後端服務、寫全棧服務都可以用得上,經驗直接復用
問題來了,如果我想獲取路由的信息,設置返回的信息等技術細節,我應該怎麼學會呢?問 GPT,GPT 不知道這麼新的東西。只能看文檔和教程。
Nitro 文檔就幾頁,沒中文,功能介紹跟 ppt 像的,也沒有 playground,看的眼睛都花了,也沒找到 api 索引。生氣,guide+api 索引 + playground + 項目實戰不應該標配嘛!
不過我是高手,好在我知道 Nitro ([[速通 - unjs 家族的 Web 框架 Nitro]]) 是 unjs/h3 ([[unjs_h3]]) 的封裝,h3 提供低級 api,nitro 提供高級封裝開箱即用。所以我應該去看 h3 的文檔,聰明如我。
github 搜索 unjs h3,打開 https://github.com/unjs/h3 結果...
算了,告訴你吧,api 索引在這裡,在 h3 長長文檔的最下方。
如何設定返回值時,指定 content-type 為 svg?自己試。結論是使用 send
方法。
對了,如果你是 Nuxt 用戶,那麼你需要按照這個路線學習 Nuxt 文檔 - Nitro 文檔 - h3 文檔。
騰訊 Serverless 函數體驗贼差#
之前文檔也提到過 ([[速通 - 騰訊雲雲函數 scf]]),作為國內大廠,值得信賴,而且做了也不是一年兩年了。
結果體驗贼差,我以為的雲函數上線過程:
- 授權 github 信息,讀取項目
- 選擇項目源碼,自動或者手動選擇基本信息,幫我做好構建
- 拿到交付產物,上傳到雲端
- 給我一個地址,我一點擊就訪問通了
- 告訴我你可以自定義網址,為了安全和推廣其他服務,建議你開通網關鑑權、監控、日誌之類的
- 用吧,額度不夠可以買套餐
挺合理的,服務好用戶,也能帶一帶其他商業服務。
實際上不是,每一步都有大量的錯誤,大量的延伸技術概念,大量的問號幫助文檔。生怕我覺得前端上線個服務挺簡單,沒有技術難度。
無力吐槽,算了,不寫了,好在走過來了。
後面單獨開個文檔介紹吧。
DNSPOD 扭扭捏捏不融入騰訊雲#
DNSPOD 被騰訊雲收購了,現在屬於騰訊雲。即便我域名、備案、解析都在騰訊雲和 DNSPOD,我開通 DNS 解析,還是要重複開頁面、登錄、掃碼、編寫。
麻煩死了。
自己愚蠢文件放錯了位置#
這個看我源碼就知道,為了目錄好看,我包了一層,結果腦抽忘了,一些文件放外層了,怎麼都拿不到,嘿嘿,後面不同項目開不同的窗口,亂了就不好了。
satori 不支持 css 和圖片的特性#
因為 svg 技術限制,satori 不支持 z-index,不支持 transfrom,必須全程使用 flex 布局,字體必須指定,不支持 webp/avif 等圖片格式,不支持 hsl 顏色格式。
怎麼說呢,好在東西不複雜,樣式改改也就能用了。
我 banner 不是有一個小人頭像麼,svg 是要通過 base64 內嵌進去的,體積會變大。為了減少圖片體積,我嘗試轉 svg,轉 avif/webp 等格式,都不是很滿意。
最後用的 png 顏色減少,感覺 24 色就足夠了,輕鬆讓 png 縮小了五六倍,肉眼看不出差異,挺好的。
總結展望#
東西做完了,感受如何,昨天都氣瘋了,今天算寫完了。
下次還來,希望輕車熟路,能快一點。
做國內的雲服務雖然有價格戰,但還是有位置的,業務還是要專注、做好。服務使用流暢應該是標配的。