关于在页面中同时支持日间/夜间模式的图片时一些笔记

陪她去流浪 桃子 2024年05月26日 编辑 阅读次数:782

之前在做“在博客文章中支持渲染 PlantUML 的功能”时,为了同时支持日间和夜间模式,我的做法很简单粗暴且原生: 同时嵌入两张图片,夜间的默认不显示,然后通过媒体查询来切换显示:

1
2
3
4
5
svg.plantuml.dark { display: none; }
@media (prefers-color-scheme: dark) {
    svg.plantuml:not(.dark) { display: none;  }
    svg.plantuml:(.dark)    { display: block; }
}

今日在 GitHub 也这样操作时,突然意识到行不通,因为 GitHub 不支持写 CSS。 然后搜索了一下,早期的做法是:

1
2
![dark](dark.png#gh-dark-mode-only)
![light](light.png#gh-light-mode-only)

这种方法其实很简单也很巧妙:

  • 类似 #gh-dark-mode-only 这种 URL Fragment 可以在将 Markdown 渲染成 HTML 时通过判断来生成适应日间/夜间的内容;
  • 这种 Fragment 不会发送到源图片服务器,所以不会对来源图片服务器的请求有不同的行为(比如请求两次);
  • 完全兼容标准的 Markdown 语法。

看似非常完美吧?但是,这个方法现在已经被 Deprecated 掉了。 我没找到非常官方的原因,但是可以猜测:

  • 如果代码被镜像到第三方,那么第三方大概率是不认识这个 #gh-dark-mode-only 东西的,所以就会导致两张图片同时被显示出来,那……势必是非常困扰读者的。

后面,标准的做法1就是换成 HTML 原生的 <picture> 元素:

1
2
3
4
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="dark.png">
  <img src="light.png">
</picture>

如果 <source> 列表有更合适的,就先用,否则用默认的 <img> 中的 src 来显示:

<picture>:

The <picture> HTML element contains zero or more <source> elements and one <img> element to offer alternative versions of an image for different display/device scenarios.

The browser will consider each child <source> element and choose the best match among them. If no matches are found—or the browser doesn't support the <picture> element—the URL of the <img> element's src attribute is selected. The selected image is then presented in the space occupied by the <img> element.

看起来完全符合大家的需求。

免不了“我的但是” ……我的但是来得很快:

  • SVG 也算作一种图片吧?<picture> 那能支持 <svg> 吗?结果是:不能。

    我是指内嵌的那种 <svg>,不是把 SVG 链接作为 <img>src 来使用。因为 <svg> 本身是一种非常“活生生”的对象,它也有样式、有事件、有动画、有按钮、有文本可以选择和复制,所以不能这样埋没人家的天赋。我希望它有天能在我这里发光发热。

  • 竟然敢在支持手动切换页面主题的情况下用 @media 查询?

    众所周知,prefers-color-scheme 只会跟着系统(或者浏览器)的当前主题走,保持和系统一样,没法手动切换。所以,如果在系统主题是“日间”时手动把当前网站切换成“夜间”,那 prefers-color-scheme 的结果仍然是“日间”。那上面的 <picture> 算是彻底废了。

    我在这个仓库里面试了试,确实这样,好尴尬😅。 连 GitHub 都没能优雅地解决这个问题,我暂时就打住吧。

有时候,真的怀疑那群制定标准的人到底有没有真正考虑到实际使用体验😅。