feat(display): background photo groups with blurred, compressed and original versions; veil, sway and blur options in the appearance panel [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful

This commit is contained in:
justme committed 2026-10-09 04:50:11 +08:00
1 parent 725fe17a7d
commit 044ce0c339
34 files changed
+255 -33

No files matched your search

+19 -9
View File
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import bgLight from './assets/bg.jpg'
import bgDark from './assets/bg-dark.jpg'
import { getAppearance, subscribeAppearance } from './appearance'
import { backgroundUrl } from './backgrounds'
/** 取样的缩略图大小:背景本来就是预先模糊过的,缩得很小就够算平均色。 */
const SAMPLE_WIDTH = 32
@@ -46,8 +46,8 @@ function toHsl(r: number, g: number, b: number): [number, number, number] {
}
/**
* 暗色下玻璃的颜色,和盖在背景图上的底色,都从当前背景图自己的平均色算出来:
* 色相照搬图里的,饱和度打个折(免得发艳、发蓝),亮度压到很暗。写进 <html> 的 --bg-tint(玻璃)和 --bg-veil-tint(底色),
* 暗色下玻璃的颜色,和盖在背景图上的底色,都从当前背景图(选了哪组照片、哪个主题)自己的平均色算出来:
* 色相照搬图里的,饱和度保留大半,亮度跟着图本身的亮度走但压得很暗。写进 <html> 的 --bg-tint(玻璃)和 --bg-veil-tint(底色),
* tokens.css 里暗色的 --glass-tint / --glass-veil 用它们;切换主题时重新取。亮色不用这个。
*/
export function useBackgroundTint() {
@@ -58,12 +58,18 @@ export function useBackgroundTint() {
const image = images.get(url)
const average = image && averageColor(image)
if (!average) return
const [h, s] = toHsl(...average)
root.style.setProperty('--bg-tint', `hsl(${h.toFixed(0)} ${(s * 55).toFixed(0)}% 13%)`)
root.style.setProperty('--bg-veil-tint', `hsl(${h.toFixed(0)} ${(s * 45).toFixed(0)}% 8%)`)
const [h, s, l] = toHsl(...average)
// 玻璃:色相照搬,饱和度保留大半(上限 45%),亮度跟着图本身的亮度走(图越亮,玻璃越亮,10%–22%);
// 底色(盖在背景图上的遮罩)同理,但更暗(6%–14%)。所以换一张照片,暗色玻璃的颜色和深浅都会跟着变
const clamp = (value: number, low: number, high: number) => Math.max(low, Math.min(high, value))
const glass = clamp(9 + l * 26, 10, 22)
const veil = clamp(5 + l * 14, 6, 14)
root.style.setProperty('--bg-tint', `hsl(${h.toFixed(0)} ${clamp(s * 90, 0, 45).toFixed(0)}% ${glass.toFixed(1)}%)`)
root.style.setProperty('--bg-veil-tint', `hsl(${h.toFixed(0)} ${clamp(s * 75, 0, 38).toFixed(0)}% ${veil.toFixed(1)}%)`)
}
const update = () => {
const url = root.dataset.theme === 'dark' ? bgDark : bgLight
// 取样用预先模糊的那个版本:和不模糊的原图平均色一样,图小、取得快
const url = backgroundUrl(getAppearance().photo, root.dataset.theme === 'dark', true, false)
if (images.has(url)) {
apply(url)
return
@@ -76,6 +82,10 @@ export function useBackgroundTint() {
update()
const observer = new MutationObserver(update)
observer.observe(root, { attributes: true, attributeFilter: ['data-theme'] })
return () => observer.disconnect()
const unsubscribe = subscribeAppearance(update)
return () => {
observer.disconnect()
unsubscribe()
}
}, [])
}