更新依赖项及历史人物相关内容资源
- 更新 package.json 和 package-lock.json 中的依赖项 - 新增/更新陈云、黄炎培、鲁迅、孙中山的音频文件 - 新增场景和组件的图片资源 - 清理未使用的音频和图片文件 - 优化多个组件和页面的实现逻辑
This commit is contained in:
1 parent
b1d55b9464
commit
e0fa47d21a
629 files changed
+24390
-2991
No files matched your search
@@ -0,0 +1,143 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { cn, getBackgroundImage } from '@/lib/utils'
|
||||
|
||||
export type HintPositionOffset = {
|
||||
/** 相对窗口中心点的水平偏移(px) */
|
||||
x?: number
|
||||
/** 相对窗口中心点的垂直偏移(px) */
|
||||
y?: number
|
||||
}
|
||||
|
||||
export interface ShowHintOptions {
|
||||
/** 自动消失时间(ms)。默认 2000 */
|
||||
durationMs?: number
|
||||
/** 是否常驻(常驻时不会自动消失) */
|
||||
persistent?: boolean
|
||||
/** 相对中心点微调位置 */
|
||||
offset?: HintPositionOffset
|
||||
/** 自定义层级(默认 60) */
|
||||
zIndex?: number
|
||||
}
|
||||
|
||||
type HintState = {
|
||||
visible: boolean
|
||||
text: string
|
||||
options: Required<Pick<ShowHintOptions, 'durationMs' | 'persistent' | 'offset' | 'zIndex'>>
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS: HintState['options'] = {
|
||||
durationMs: 2000,
|
||||
persistent: false,
|
||||
offset: { x: 0, y: 0 },
|
||||
zIndex: 60,
|
||||
}
|
||||
|
||||
let current: HintState = {
|
||||
visible: false,
|
||||
text: '',
|
||||
options: DEFAULT_OPTIONS,
|
||||
}
|
||||
|
||||
let hideTimer: number | null = null
|
||||
const listeners = new Set<(s: HintState) => void>()
|
||||
|
||||
function emit(next: HintState) {
|
||||
current = next
|
||||
listeners.forEach((l) => l(current))
|
||||
}
|
||||
|
||||
function clearHideTimer() {
|
||||
if (hideTimer) {
|
||||
window.clearTimeout(hideTimer)
|
||||
hideTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
export function showHint(text: string, options?: ShowHintOptions) {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
clearHideTimer()
|
||||
|
||||
const mergedOptions: HintState['options'] = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...(options ?? {}),
|
||||
offset: {
|
||||
...DEFAULT_OPTIONS.offset,
|
||||
...(options?.offset ?? {}),
|
||||
},
|
||||
}
|
||||
|
||||
emit({
|
||||
visible: true,
|
||||
text,
|
||||
options: mergedOptions,
|
||||
})
|
||||
|
||||
if (!mergedOptions.persistent) {
|
||||
hideTimer = window.setTimeout(() => {
|
||||
hideHint()
|
||||
}, mergedOptions.durationMs)
|
||||
}
|
||||
}
|
||||
|
||||
export function hideHint() {
|
||||
if (typeof window === 'undefined') return
|
||||
clearHideTimer()
|
||||
emit({ ...current, visible: false })
|
||||
}
|
||||
|
||||
export function HintHost() {
|
||||
const [state, setState] = useState<HintState>(current)
|
||||
|
||||
useEffect(() => {
|
||||
listeners.add(setState)
|
||||
return () => {
|
||||
listeners.delete(setState)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const offsetX = state.options.offset.x ?? 0
|
||||
const offsetY = state.options.offset.y ?? 0
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none" style={{ zIndex: state.options.zIndex }}>
|
||||
<AnimatePresence>
|
||||
{state.visible && state.text && (
|
||||
<motion.div
|
||||
key="hint"
|
||||
className="absolute left-1/2"
|
||||
style={{
|
||||
top: `calc(50% + ${offsetY}px)`,
|
||||
x: `calc(-50% + ${offsetX}px)`,
|
||||
zIndex: state.options.zIndex,
|
||||
}}
|
||||
initial={{ opacity: 0, y: 8, filter: 'blur(2px)' }}
|
||||
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
|
||||
exit={{ opacity: 0, y: 8, filter: 'blur(2px)' }}
|
||||
transition={{ duration: 0.22, ease: 'easeOut' }}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'inline-flex items-center justify-center',
|
||||
'px-30 py-2',
|
||||
// 'bg-linear-to-r from-transparent from-1% via-[#C0945A] via-50% to-transparent to-99% text-white',
|
||||
'whitespace-nowrap text-white',
|
||||
'text-3xl font-semibold tracking-wide',
|
||||
'select-none'
|
||||
)}
|
||||
style={{
|
||||
backgroundImage: getBackgroundImage('components/提示条'),
|
||||
backgroundSize: '100% 100%',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
{state.text}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user