九宫格优化
This commit is contained in:
19
vite-tailwindcss/src/utils/clientId.js
Normal file
19
vite-tailwindcss/src/utils/clientId.js
Normal 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()
|
||||
}
|
||||
}
|
||||
48
vite-tailwindcss/src/utils/danmakuColors.js
Normal file
48
vite-tailwindcss/src/utils/danmakuColors.js
Normal 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: '损友文风' },
|
||||
]
|
||||
44
vite-tailwindcss/src/utils/musicPick.js
Normal file
44
vite-tailwindcss/src/utils/musicPick.js
Normal 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
|
||||
}
|
||||
52
vite-tailwindcss/src/utils/relativeTime.js
Normal file
52
vite-tailwindcss/src/utils/relativeTime.js
Normal 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()}日`
|
||||
}
|
||||
Reference in New Issue
Block a user