仰望星辰工作室

starmusic-pc

starmusic-pc/ frontend/src/common/background.js 4.5 KB · 126 行 原始文件
1/**
2 * 自定义背景图片(PC 版)
3 *
4 * 图片持久化:统一存为 base64 dataURL(CSS background-image 用 base64 不依赖本地路径协议)。
5 * - PC 端:优先调用 Wails 原生对话框选图,再在渲染侧用 canvas 等比压缩为 jpeg 后转 base64;
6 * 原生桥不可用时(浏览器调试)回退 <input type=file>。
7 * 开关与模糊程度存设置项(common.bgImageEnabled / common.bgImageBlur)。
8 */
9import { getSetting } from './setting.js'
10import { getData, saveData, removeData } from './data.js'
11import { pickImageNative, pickImageByInput } from '../uni/native.js'
12
13/** 背景图数据存储键 */
14export const BG_IMAGE_KEY = 'starmusic_bg_image'
15
16/** 压缩:最长边与 jpeg 质量 */
17const MAX_SIDE = 1920
18const JPEG_QUALITY = 0.82
19/** base64 体积上限(约 4MB,超出会拖慢存储读写) */
20const MAX_BASE64_LENGTH = 4 * 1024 * 1024
21
22/** 内存缓存:避免每个页面初始化时同步读取大字符串 */
23let cachedImage = null
24
25/** 当前背景图(base64 dataURL) */
26export const getBgImage = () => {
27 if (cachedImage === null) cachedImage = getData(BG_IMAGE_KEY, '') || ''
28 return cachedImage
29}
30
31/** 写入背景图(供原生选图后直接注入 dataURL) */
32export const setBgImageFromDataUrl = (dataUrl) => {
33 cachedImage = dataUrl || ''
34 if (cachedImage) saveData(BG_IMAGE_KEY, cachedImage)
35 else removeData(BG_IMAGE_KEY)
36 return cachedImage
37}
38
39/** 背景图是否可用(开关打开且已选图) */
40export const isBgImageOn = () => !!getSetting()['common.bgImageEnabled'] && !!getBgImage()
41
42/**
43 * 背景图启用时下发给页面根元素的"内容层透明"变量。
44 * 各页面容器以 var(--card-bg, #fff) 之类形式引用,未启用背景图时回退原本的不透明底色,
45 * 从而做到"只在有背景图时半透明",不影响默认外观。
46 */
47export const buildBgVars = () => {
48 if (!isBgImageOn()) return {}
49 return {
50 // 卡片 / 栏 / 列表项
51 '--card-bg': 'rgba(255, 255, 255, 0.72)',
52 // 弹层(需要更高的可读性)
53 '--card-bg-strong': 'rgba(255, 255, 255, 0.94)',
54 // 浅色填充(输入框 / 未选中标签 / 占位图)
55 '--fill-bg': 'rgba(255, 255, 255, 0.48)',
56 // 次要 / 提示文字:卡片透出背景图后灰色对比度不足,加深到黑色系
57 '--text-secondary': '#1f2329',
58 '--text-muted': '#33373d',
59 }
60}
61
62/**
63 * 构建页面背景层的内联样式(未启用或未选图时返回 null)
64 * 返回 null 时页面不渲染背景层,等同于普通背景色
65 */
66export const buildBgStyle = () => {
67 if (!isBgImageOn()) return null
68 const url = getBgImage()
69 if (!url) return null
70 const blur = Number(getSetting()['common.bgImageBlur'])
71 return {
72 backgroundImage: `url("${url}")`,
73 filter: blur > 0 ? `blur(${blur}px)` : 'none',
74 }
75}
76
77// ============ 选择 / 移除图片 ============
78
79/** 等比压缩为 jpeg base64(同时避免超出存储容量) */
80const compressImage = (src) =>
81 new Promise((resolve, reject) => {
82 const img = new Image()
83 img.onload = () => {
84 try {
85 const scale = Math.min(1, MAX_SIDE / Math.max(img.width, img.height))
86 const width = Math.max(1, Math.round(img.width * scale))
87 const height = Math.max(1, Math.round(img.height * scale))
88 const canvas = document.createElement('canvas')
89 canvas.width = width
90 canvas.height = height
91 const ctx = canvas.getContext('2d')
92 ctx.drawImage(img, 0, 0, width, height)
93 resolve(canvas.toDataURL('image/jpeg', JPEG_QUALITY))
94 } catch (e) {
95 reject(new Error('图片处理失败'))
96 }
97 }
98 img.onerror = () => reject(new Error('图片读取失败'))
99 img.src = src
100 })
101
102/**
103 * 选择并设置背景图
104 * 优先走 Wails 原生文件对话框;不可用时回退浏览器文件选择
105 * @returns {Promise<string>} 最终存储的 dataURL
106 */
107export const chooseBgImage = async () => {
108 const source = (await pickImageNative('选择背景图片')) || (await pickImageByInput('image/*'))
109 const tempPath = source && (source.dataUrl || source.tempFilePath)
110 if (!tempPath) throw new Error('未选择图片')
111 const base64 = await compressImage(tempPath)
112 if (base64.length > MAX_BASE64_LENGTH) throw new Error('图片过大,请选择更小的图片')
113 setBgImageFromDataUrl(base64)
114 return base64
115}
116
117/** 移除背景图 */
118export const clearBgImage = () => {
119 setBgImageFromDataUrl('')
120}
121
122/** 重新从存储加载(外部写入后同步内存缓存) */
123export const reloadBgImage = () => {
124 cachedImage = null
125 return getBgImage()
126}