写文章时插Afilmory相册的照片,这是作者已经支持了的,但受限于我是静态方式部署的Afilmory,只能对yohaku前端的代码进行修改以支持,同时我也把样式修改成了我想要的,以相册卡堆叠的形式展现。效果还不错~前置是我另一篇 Afilmory:用阿里云 OSS 搭建原图私有化的静态画廊网站,按那篇部署完,整体是私有化原图的架构,才适用于这篇文章对yohaku的改动。0. 效果在mix core后台可以正常插入Afilmory组件了选择了多图时,可以在yohaku上以堆叠的卡片显示照片就像这样,点击左右可以有个丝滑的切换照片,手机端的体验也是一致的点击一张图片后,仍然在博客内大图预览,不跳转到Afilmory去为了保证浏览文章的一致性,我觉得新开页面去到afilmory浏览有点打断读者思路,他需要在不同标签页间切换,所以我把这个选择权给到读者,想去Afilmory站点查看,就在大图展开后点击下方View去查看,这样既能看到摄影的原图,又能保证加载速度。1. 静态部署缺的是一个接口Mix Space 的编辑器自带 Afilmory 区块,插入时填一个相册地址,它会去那个地址读照片清单。官方版本这些接口由 Afilmory 后端提供,静态部署没有后端,公开桶只能返回固定文件,编辑器就连不上。缺的接口是这四个:方法路径用途GET`/api/manifest`全量清单,编辑器选片面板用GET`/api/manifest/photos?ids=a,b,c`按 id 批量取POST`/api/manifest/photos/search`按标签、相机、日期筛选GET`/api/manifest/photos/:id`单张其中 ?ids= 和 POST 这两个,静态桶伺候不了:OSS 会忽略查询串,POST 直接返回 405。所以必须有个东西在跑。我的做法是加一个函数计算函数,把构建产出的照片清单打进代码包,按上面的路由返回 JSON。博客那边不用改任何代码就能连上。2. 数据实际怎么走配好之后有三条路径,走的地方完全不同:清单接口:media-auth.example.com/api/manifest*,缩略图:example.com/thumbnails/xxx.webp,公开桶 看到的是同一批文件原图:media-auth.example.com/original/xxx,签名函数派发带 auth_key 的临时地址,302 到私有桶 CDN原图这条和在相册站点开大图完全一样,没有旁路。不带签名这里有个容易搞混的点。编辑器里填的相册地址会被同时拿去拼三样东西:接口、图片地址、详情页链接。接口只存在于函数域名上,所以这个地址得填函数的域名,不是相册主站。至于缩略图,函数在返回清单时已经把地址改写成主站的绝对地址了,不会绕回函数。3. 加函数和路由这部分在另一篇的「可选:在博客中引用相册」一章,这里只列函数 afilmory-manifest-api,Node.js 20,事件函数,入口 index.handler,0.35 vCPU / 512 MB,最小实例数 0。环境变量两个:变量名说明`ALLOW_ORIGINS`博客前台和后台地址,逗号分隔,要带 `https://``SITE_BASE_URL`相册主站地址,用于补全缩略图和跳转路由挂在签名函数已有的自定义域名下,不用新域名。「域名管理」→ 点进 `media-auth.example.com` →「路由配置」,加三条指向新函数:路径函数`/api/*`afilmory-manifest-api`/photos/*`afilmory-manifest-api`/`(精确路径,不带 `*`)afilmory-manifest-api原来的 /* → 签名函数保留不动。第三条是我后来补的。「查看全部」和标签筛选的链接直接拼在相册地址上,长这样 https://media-auth.example.com/?tags=xxx。没有这条路由它会落到 /*,返回一段 {"message":"Not Found"} 的 JSON。最后在服务器的 deploy/aliyun-static/.env 加一行,之后 两个函数:BASH复制FC_MANIFEST_API_FUNCTION_NAME=afilmory-manifest-apiTip如果博客端要用 JavaScript 读原图的字节(比如显示下载进度条),签名函数的 ALLOW_ORIGIN 里也得加上博客域名,逗号分隔。只用 <img> 显示不受影响。4. 配到这里就能用了接口通了之后,编辑器里插入 Afilmory 区块,填函数域名,选片面板就能列出照片。文章里会按 Mix Space自带的渲染显示,支持网格、瀑布流和横向滑动三种布局,单张卡片,点击跳转到相册。到这一步不需要动博客一行代码。下面是我自己又改的部分,不做也不影响。5. 改渲染自带的渲染有两个我想改的地方。多图是横向滚动条,一排缩略图铺开,和文章的阅读节奏有点冲突。点击会跳出文章去相册,读者往往就不回来了。我改成了这样:多图显示为一叠错落的相纸,点左右翻,最上面那张会上浮、左移、落到最底下,像翻一叠照片卡片是正方形的,照片按自己的比例嵌在里面,四周留白就是正方形是唯一对 3:2 和 2:3一样公平的比例,两种朝向显示面积相同,都不裁切点击不跳转,原地弹出大图。旁边放光圈、快门、ISO、焦段四项,竖图在右、横图在下,手机上只留一个去相册的入口大图支持滚轮、触控板捏合、手机双指缩放和拖拽平移,双击快速放大加载进度是真的,流式读 Content-Length 算百分比,读不到长度就老实显示转圈,不编数字代码在这个分支,整个改动集中在一个目录里:复制https://github.com/specialhua/yohaku-oss 分支 afilmory-deck packages/rich-content/src/lexical/biz/afilmory/6. 子模块Yohaku 的渲染代码在 yohaku-oss 这个子模块里,它指向 Innei 的公开仓库,你没有写权限。直接在里面提交会得到一个只存在于本机的commit,父仓库的指针推上去就悬空了,别人(包括你的 CI)拉不到。做法是 fork 一份,把子模块指过去:BASH复制gh repo fork Innei/Yohaku --fork-name yohaku-oss --clone=false cd yohaku-oss git remote rename origin upstream git remote add origin https://github.com/<你的用户名>/yohaku-oss.git git remote set-url --push origin git@github.com:<你的用户名>/yohaku-oss.git git checkout -b afilmory-deck git push -u origin afilmory-deck cd .. git config -f .gitmodules submodule.yohaku-oss.url https://github.com/<你的用户名>/yohaku-oss.git git submodule sync yohaku-oss git add .gitmodules && git commit -m "chore: 子模块指向自己的 fork"Warning.gitmodules 里必须是 HTTPS 地址。GitHub Actions 的 runner 上没有你的 SSH key,写成 git@github.com: 会让 actions/checkout 的子模块那一步直接挂掉,镜像构建失败,服务器拉到的还是旧镜像。上面用 set-url --push 把推送单独设成 SSH,抓取走 HTTPS,你本地推代码的习惯不用变。以后改完代码要提交两次,子模块和父仓库的指针得在一起:BASH复制bashcd yohaku-oss && git add -A && git commit -m "..." && gi cd .. && git add yohaku-oss && git commit -m "chore: bump yohaku-oss" && git push同步上游更新时多一步合并。git fetch upstream && git merge upstream/main,冲突基本只会出现在dist/rich.css,那是编译产物,别手动合,直接重新 pnpm --filter @yohaku/rich-content build:css生成。7. 几个值得记一下的坑React 的合成事件沿组件树冒泡,不走 DOM 树。 灯箱用 createPortal 挂到了 document.body,DOM 上已经在外面了,但只要 JSX 里它写在那个 <a> 内部,灯箱里的每一次点击仍然会冒泡回链接的 onClick。我第一版把「去相册」那个按钮点废了,查了半天。灯箱得渲染成链接的兄弟节点。will-change: transform 会让放大发虚。 这个提示把元素提升为独立合成层,而合成层按自身布局尺寸光栅化一次,之后的scale() 由合成器拉伸那张缓存位图,放大看到的是被放大的像素。桌面浏览器手势结束后通常会重新光栅化,移动端合成器最懒,所以只在手机上明显。去掉就好了。Tailwind preflight 里的 img { height: auto } 会压过 size-full。 在文章上下文里,图片高度塌回原始比例,贴在盒子顶部,下面留一块空。给需要填满的图片内联写 width/height: 100%,内联样式必胜。onWheel 里的 preventDefault() 不生效。 React 在根节点上以被动方式挂 wheel 监听,要自己 addEventListener(..., { passive: false }),否则缩放时页面跟着滚。双击会顺手选中照片。 浏览器是在第二次 mousedown 上决定建立选区的,这时 dblclick 还没派发。判断 event.detail > 1 并取消默认行为即可,user-select: none 单独不够可靠。8. 验证BASH复制# 接口通不通 curl -s https://media-auth.example.com/api/health # 「查看全部」会不会打开 JSON curl -sI https://media-auth.example.com/ | grep -i location # 跨域头有没有回显你的博客域名 curl -s -o /dev/null -D- -H "Origin: https://blog.exampl https://media-auth.example.com/api/manifest | grep -i n # 原图链路没被新路由挡掉 curl -sI https://media-auth.example.com/original/<某张照片id> | grep -i location最后一条应该返回带 auth_key 的 CDN 地址。四条都对,就可以去后台插一组照片试试了。9. 写在最后接口那部分是通用的,任何能发 HTTP 请求的博客系统都能接,不限于 Mix Space。渲染那部分是我自己的偏好,未必要照抄,但那几个坑大概率会遇到,写在这儿省一点时间,供参考。
写文章时插Afilmory相册的照片,这是作者已经支持了的,但受限于我是静态方式部署的Afilmory,只能对yohaku前端的代码进行修改以支持,同时我也把样式修改成了我想要的,以相册卡堆叠的形式展现。效果还不错~
前置是我另一篇 Afilmory:用阿里云 OSS 搭建原图私有化的静态画廊网站,按那篇部署完,整体是私有化原图的架构,才适用于这篇文章对yohaku的改动。
0. 效果
在mix core后台可以正常插入Afilmory组件了
选择了多图时,可以在yohaku上以堆叠的卡片显示照片
就像这样,点击左右可以有个丝滑的切换照片,手机端的体验也是一致的
点击一张图片后,仍然在博客内大图预览,不跳转到Afilmory去
为了保证浏览文章的一致性,我觉得新开页面去到afilmory浏览有点打断读者思路,他需要在不同标签页间切换,所以我把这个选择权给到读者,想去Afilmory站点查看,就在大图展开后点击下方View去查看,这样既能看到摄影的原图,又能保证加载速度。
1. 静态部署缺的是一个接口
Mix Space 的编辑器自带 Afilmory 区块,插入时填一个相册地址,它会去那个地址读照片清单。官方版本这些接口由 Afilmory 后端提供,静态部署没有后端,公开桶只能返回固定文件,编辑器就连不上。
缺的接口是这四个:
方法
路径
用途
GET
`/api/manifest`
全量清单,编辑器选片面板用
GET
`/api/manifest/photos?ids=a,b,c`
按 id 批量取
POST
`/api/manifest/photos/search`
按标签、相机、日期筛选
GET
`/api/manifest/photos/:id`
单张
其中 ?ids= 和 POST 这两个,静态桶伺候不了:OSS 会忽略查询串,POST 直接返回 405。所以必须有个东西在跑。
我的做法是加一个函数计算函数,把构建产出的照片清单打进代码包,按上面的路由返回 JSON。博客那边不用改任何代码就能连上。
2. 数据实际怎么走
配好之后有三条路径,走的地方完全不同:
原图这条和在相册站点开大图完全一样,没有旁路。不带签名
这里有个容易搞混的点。编辑器里填的相册地址会被同时拿去拼三样东西:接口、图片地址、详情页链接。接口只存在于函数域名上,所以这个地址得填函数的域名,不是相册主站。至于缩略图,函数在返回清单时已经把地址改写成主站的绝对地址了,不会绕回函数。
3. 加函数和路由
这部分在另一篇的「可选:在博客中引用相册」一章,这里只列
函数 afilmory-manifest-api,Node.js 20,事件函数,入口 index.handler,0.35 vCPU / 512 MB,最小实例数 0。环境变量两个:
变量名
说明
`ALLOW_ORIGINS`
博客前台和后台地址,逗号分隔,要带 `https://`
`SITE_BASE_URL`
相册主站地址,用于补全缩略图和跳转
路由挂在签名函数已有的自定义域名下,不用新域名。「域名管理」→ 点进 `media-auth.example.com` →「路由配置」,加三条指向新函数:
路径
函数
`/api/*`
afilmory-manifest-api
`/photos/*`
afilmory-manifest-api
`/`(精确路径,不带 `*`)
afilmory-manifest-api
原来的 /* → 签名函数保留不动。
第三条是我后来补的。「查看全部」和标签筛选的链接直接拼在相册地址上,长这样 https://media-auth.example.com/?tags=xxx。没有这条路由它会落到 /*,返回一段 {"message":"Not Found"} 的 JSON。
最后在服务器的 deploy/aliyun-static/.env 加一行,之后 两个函数:
如果博客端要用 JavaScript 读原图的字节(比如显示下载进度条),签名函数的 ALLOW_ORIGIN 里也得加上博客域名,逗号分隔。只用 <img> 显示不受影响。
4. 配到这里就能用了
接口通了之后,编辑器里插入 Afilmory 区块,填函数域名,选片面板就能列出照片。文章里会按 Mix Space
自带的渲染显示,支持网格、瀑布流和横向滑动三种布局,单张卡片,点击跳转到相册。
到这一步不需要动博客一行代码。下面是我自己又改的部分,不做也不影响。
5. 改渲染
自带的渲染有两个我想改的地方。多图是横向滚动条,一排缩略图铺开,和文章的阅读节奏有点冲突。点击会跳出文章去相册,读者往往就不回来了。
我改成了这样:
代码在这个分支,整个改动集中在一个目录里:
6. 子模块
Yohaku 的渲染代码在 yohaku-oss 这个子模块里,它指向 Innei 的公开仓库,你没有写权限。直接在里面提交会得到一个只存在于本机的commit,父仓库的指针推上去就悬空了,别人(包括你的 CI)拉不到。
做法是 fork 一份,把子模块指过去:
.gitmodules 里必须是 HTTPS 地址。GitHub Actions 的 runner 上没有你的 SSH key,写成 git@github.com: 会让 actions/checkout 的子模块那一步直接挂掉,镜像构建失败,服务器拉到的还是旧镜像。上面用 set-url --push 把推送单独设成 SSH,抓取走 HTTPS,你本地推代码的习惯不用变。
以后改完代码要提交两次,子模块和父仓库的指针得在一起:
同步上游更新时多一步合并。git fetch upstream && git merge upstream/main,冲突基本只会出现在dist/rich.css,那是编译产物,别手动合,直接重新 pnpm --filter @yohaku/rich-content build:css生成。
7. 几个值得记一下的坑
React 的合成事件沿组件树冒泡,不走 DOM 树。 灯箱用 createPortal 挂到了 document.body,DOM 上已经在外面了,但只要 JSX 里它写在那个 <a> 内部,灯箱里的每一次点击仍然会冒泡回链接的 onClick。我第一版把「去相册」那个按钮点废了,查了半天。灯箱得渲染成链接的兄弟节点。
will-change: transform 会让放大发虚。 这个提示把元素提升为独立合成层,而合成层按自身布局尺寸光栅化一次,之后的scale() 由合成器拉伸那张缓存位图,放大看到的是被放大的像素。桌面浏览器手势结束后通常会重新光栅化,移动端合成器最懒,所以只在手机上明显。去掉就好了。
Tailwind preflight 里的 img { height: auto } 会压过 size-full。 在文章上下文里,图片高度塌回原始比例,贴在盒子顶部,下面留一块空。给需要填满的图片内联写 width/height: 100%,内联样式必胜。
onWheel 里的 preventDefault() 不生效。 React 在根节点上以被动方式挂 wheel 监听,要自己 addEventListener(..., { passive: false }),否则缩放时页面跟着滚。
双击会顺手选中照片。 浏览器是在第二次 mousedown 上决定建立选区的,这时 dblclick 还没派发。判断 event.detail > 1 并取消默认行为即可,user-select: none 单独不够可靠。
8. 验证
最后一条应该返回带 auth_key 的 CDN 地址。四条都对,就可以去后台插一组照片试试了。
9. 写在最后
接口那部分是通用的,任何能发 HTTP 请求的博客系统都能接,不限于 Mix Space。渲染那部分是我自己的偏好,未必要照抄,但那几个坑大概率会遇到,写在这儿省一点时间,供参考。