让博客支持实况照片(Live Photos)

on 2026-05-25

iPhone 拍的实况照片(Live Photo)会连带记录按下快门前后约三秒的画面。导出到电脑、上传到博客之后,这段动态就丢了,剩下一张普通的静态图——那一刻的风、表情、光线都没了。

于是我给博客加了实况照片的支持。

先看效果

桌面端把鼠标移上去,移动端长按,就会播放——这就是这张实况照片在按下快门那两秒里真实捕捉的画面。松开后回到静态图,左上角的 ◉ LIVE 角标和 iOS 上一致。

它是怎么工作的

一张实况照片其实是 一张静态图 + 一段约三秒的视频。页面默认只显示静态图,交互时再把视频淡入播放:

  • 桌面:悬停播放,移开复位
  • 移动:长按播放,松手复位
  • 键盘:聚焦后按空格 / 回车切换

视频用了 muted loop playsinline preload="none"——默认不下载,只有真正交互时才加载,不浪费流量。

我没有引入 Apple 官方的 LivePhotosKit(那是一个约 200KB、需要从 Apple CDN 加载的库),而是自己写了大约 40 行原生 JS 加一段 CSS,完全自托管,符合本站 useCDN = false 的取向。代价是动效不是 Apple 那种弹簧式的逐帧擦除,但「静态图 → 动态」的体验已经很接近。

怎么用

三步。

1. 转换素材。 iPhone 导出的是 HEIC 静态图加 MOV 视频,浏览器既不认 HEIC、也未必能播 MOV,所以先转成网页能用的 JPEG + MP4:

./scripts/livephoto.sh ~/IMG_1234.HEIC ~/IMG_1234.MOV 名字

脚本会在 static/imgs/livephotos/ 下生成 名字.jpg名字.mp4,并直接打印出可以粘贴的短代码(连宽高都填好了)。

2. 在文章里开启。 在 frontmatter 的 [extra] 下加一行——这样播放脚本只会在用到的文章里加载:

[extra]
livephoto = true

3. 正文里嵌入。 把脚本打印的短代码贴进来:

{{ livephoto(still="/imgs/livephotos/名字.jpg", video="/imgs/livephotos/名字.mp4", alt="说明", width=1080, height=1440) }}

width / height 用来提前占好比例,图片加载时页面不会跳动。

实现要点

几个值得记一笔的地方:

  • 短代码 templates/shortcodes/livephoto.html:渲染一个 <figure>,里面是叠在一起的静态图 <img><video>,外加一个纯 CSS 画的 LIVE 角标。JS 没加载时,静态图照样显示,优雅降级。
  • 按需加载脚本:复用了本站已有的「按文章开关」模式(和 KaTeX 一样,page.extra.<flag>),没开 livephoto 的页面一行 JS 都不会多加载。
  • 和全局样式打架:站点有几条全局规则会漏到实况照片上——img { border }figure img { max-height }、桌面端 figure { padding }——都得在 .livephoto 里逐一覆盖,否则静态图和视频两层会对不齐。
  • 转换脚本用 macOS 自带的 sips 做 HEIC → JPEG,用 ffmpeg 做 MOV → MP4(yuv420p + +faststart,并去掉音轨,因为实况照片本就是静音的)。

小结

加完之后,博客里那些来自 iPhone 的照片终于能动起来了。代码很少,依赖为零,按需加载。下次写游记,就能把按下快门那一刻的风一起放进来。