九宫格优化

This commit is contained in:
李琦
2026-08-01 16:55:39 +08:00
parent 8d015f8b37
commit 9950b86841
18 changed files with 1465 additions and 38 deletions

View File

@@ -0,0 +1,19 @@
const KEY = 'wedding_client_id_v1'
function uuid() {
if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID()
return `c_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`
}
export function getClientId() {
try {
let id = localStorage.getItem(KEY)
if (!id) {
id = uuid()
localStorage.setItem(KEY, id)
}
return id
} catch {
return uuid()
}
}

View File

@@ -0,0 +1,48 @@
export const DEFAULT_DANMAKU_COLOR = 'champagne'
/** @type {{ key: string, hex: string, label: string }[]} */
export const DANMAKU_COLORS = [
{ key: 'champagne', hex: '#a88c6b', label: '香槟金' },
{ key: 'blush', hex: '#e8b4b8', label: '浅粉' },
{ key: 'apricot', hex: '#e6b89c', label: '杏桃' },
{ key: 'gold', hex: '#d4a574', label: '暖金' },
{ key: 'lilac', hex: '#b5a4c9', label: '淡紫' },
{ key: 'sky', hex: '#7c9eb2', label: '雾蓝' },
{ key: 'mauve', hex: '#9b7e9a', label: '藕紫' },
{ key: 'slate', hex: '#64748b', label: '灰蓝' },
{ key: 'ink', hex: '#5c4a35', label: '墨褐' },
]
const HEX_BY_KEY = Object.fromEntries(DANMAKU_COLORS.map((c) => [c.key, c.hex]))
function hexToRgb(hex) {
const h = hex.replace('#', '')
const n = parseInt(h.length === 3 ? h.split('').map((c) => c + c).join('') : h, 16)
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 }
}
export function resolveDanmakuColor(key) {
if (key === 'rose' || key === 'sage') return HEX_BY_KEY[DEFAULT_DANMAKU_COLOR]
return HEX_BY_KEY[key] || HEX_BY_KEY[DEFAULT_DANMAKU_COLOR]
}
export function resolveDanmakuTint(key) {
const hex = resolveDanmakuColor(key)
const { r, g, b } = hexToRgb(hex)
return {
background: `rgba(${r}, ${g}, ${b}, 0.18)`,
borderColor: `rgba(${r}, ${g}, ${b}, 0.42)`,
}
}
export function normalizeDanmakuColor(key) {
if (key === 'rose' || key === 'sage') return DEFAULT_DANMAKU_COLOR
return HEX_BY_KEY[key] ? key : DEFAULT_DANMAKU_COLOR
}
export const BLESSING_STYLES = [
{ key: 'classic', label: '古风典雅' },
{ key: 'modern', label: '现代文风' },
{ key: 'funny', label: '搞怪文风' },
{ key: 'roast', label: '损友文风' },
]

View File

@@ -0,0 +1,44 @@
const STORAGE_KEY = 'wedding_music_pick_v1'
const DAY_MS = 24 * 60 * 60 * 1000
function readCache() {
try {
const raw = localStorage.getItem(STORAGE_KEY)
if (!raw) return null
const parsed = JSON.parse(raw)
if (!parsed?.url || !parsed?.ts) return null
return parsed
} catch {
return null
}
}
export function rememberMusicPick(url) {
if (!url) return
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ url, ts: Date.now() }))
} catch { /* ignore quota */ }
}
/**
* Resolve which track to play.
* When random is enabled, reuse local cache within 24h if still in list; otherwise pick randomly.
*/
export function pickMusicUrl(musicList, activeMusicUrl, musicRandomEnabled) {
const list = Array.isArray(musicList) ? musicList.filter((t) => t && t.url) : []
if (!list.length) return ''
if (!musicRandomEnabled) {
if (activeMusicUrl && list.some((t) => t.url === activeMusicUrl)) return activeMusicUrl
return list[0].url
}
const cache = readCache()
if (cache && Date.now() - cache.ts < DAY_MS && list.some((t) => t.url === cache.url)) {
return cache.url
}
const picked = list[Math.floor(Math.random() * list.length)].url
rememberMusicPick(picked)
return picked
}

View File

@@ -0,0 +1,52 @@
const WEEKDAY = ['日', '一', '二', '三', '四', '五', '六']
function startOfWeekMonday(d) {
const x = new Date(d.getFullYear(), d.getMonth(), d.getDate())
const day = x.getDay() // 0 Sun .. 6 Sat
const diff = day === 0 ? 6 : day - 1
x.setDate(x.getDate() - diff)
x.setHours(0, 0, 0, 0)
return x
}
/**
* Chinese relative time for danmaku:
* 刚刚 | x分钟前 | x小时前 | x天前 | 周X | 上周X | x周前 | X月X日 | X年X月X日
*/
export function formatRelativeTime(input, now = new Date()) {
const date = input instanceof Date ? input : new Date(input)
if (Number.isNaN(date.getTime())) return ''
const diffMs = now.getTime() - date.getTime()
if (diffMs < 0) return '刚刚'
const minute = 60 * 1000
const hour = 60 * minute
const day = 24 * hour
const week = 7 * day
if (diffMs < minute) return '刚刚'
if (diffMs < hour) return `${Math.floor(diffMs / minute)}分钟前`
if (diffMs < day) return `${Math.floor(diffMs / hour)}小时前`
if (diffMs < week) return `${Math.floor(diffMs / day)}天前`
const thisWeekStart = startOfWeekMonday(now)
const lastWeekStart = new Date(thisWeekStart)
lastWeekStart.setDate(lastWeekStart.getDate() - 7)
const dateDay = new Date(date.getFullYear(), date.getMonth(), date.getDate())
if (dateDay >= thisWeekStart) {
return `${WEEKDAY[date.getDay()]}`
}
if (dateDay >= lastWeekStart) {
return `上周${WEEKDAY[date.getDay()]}`
}
const weeksAgo = Math.floor(diffMs / week)
if (weeksAgo < 8) return `${weeksAgo}周前`
if (date.getFullYear() === now.getFullYear()) {
return `${date.getMonth() + 1}${date.getDate()}`
}
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`
}