关联内部链接:
- [[速通 - 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 缩小了五六倍,肉眼看不出差异,挺好的。
总结展望#
东西做完了,感受如何,昨天都气疯了,今天算写完了。
下次还来,希望轻车熟路,能快一点。
做国内的云服务虽然有价格战,但还是有位置的,业务还是要专注、做好。服务使用流畅应该是标配的。