渲染进程负责用户界面展示和交互,采用 Vue 3 + Element Plus 技术栈,包含多个独立窗口,每个窗口都有特定的功能和用途。
主窗口是应用的核心界面,采用侧边栏 + 主内容区的布局设计,支持多页面切换和功能模块管理。
- 侧边栏:可折叠的菜单导航,支持展开/收起切换
- 主内容区:动态加载不同功能页面,支持页面切换动画
- 自定义标题栏:支持窗口拖拽、最小化、最大化、关闭等操作
关键代码片段:
<el-container class="window-container">
<el-aside class="window-side-wrapper" :width="settingData.expandSideMenu ? '70px' : '0'">
<SideMenu v-if="settingData.expandSideMenu" />
<div class="side-expand-btn" @click="toggleExpandSideMenu">
<IconifyIcon :icon="settingData.expandSideMenu ? 'ep:caret-left' : 'ep:caret-right'" />
</div>
</el-aside>
<el-main class="window-main-wrapper">
<MainContainer />
</el-main>
</el-container>- Explore:探索页面,浏览和发现壁纸
- Search:搜索页面,关键词/语义搜索壁纸
- Collections:合集页面,用户合集、系统策展与猜你喜欢
- Favorites:收藏页面,管理收藏的壁纸
- History:历史页面,查看浏览历史
- Words:词库页面,管理搜索关键词
- Setting:设置页面,应用配置管理
- Utils:工具页面,实用功能集合
- About:关于页面,应用信息展示
关键代码片段:
const componentDict = {
Explore,
Search,
Favorites,
History,
Words,
Setting,
Utils,
About
}- 支持菜单启用/禁用配置
- 动态过滤显示启用的菜单项
- 支持主进程页面跳转事件监听
- 集成二维码生成功能(H5 服务地址)
关键代码片段:
const enabledMenus = computed(() => {
const list = menuList.value.filter((item) => item.placement.includes('sideMenu'))
if (!settingData.value?.enabledMenus?.length) {
return list
}
return list.filter((item) => {
if (item.canBeEnabled) {
return settingData.value.enabledMenus.includes(item.name)
}
return true
})
})探索页(ExploreCommon.vue)与合集页(Collections.vue)卡片内支持视频缩略图播放:
- 静音控制:
ResourceExploreCard.vue右下角音量按钮,默认静音,点击切换 - 状态管理:
ExploreCommon.vue使用videoMutedByIndex,监听playing事件与<video>.muted同步 - 全屏预览:
ViewVideo.vue全屏播放;关闭按钮默认隐藏,鼠标 hover 视频区域或 键盘 focus-visible 时显示
- 探索顶栏:评分筛选、
useSemanticSearch语义搜索开关 - 卡片菜单:找相似 调用
HybridSimilarSearch(画面向量优先) - 详见 AI 能力
- 组件:
NsfwContentMask.vue+usePrivacyNsfwMask.mjs - 接入:
ExploreCommon.vue、Collections.vue - 策略见 privacy-and-sensitive-content.md
悬浮球提供快捷操作功能,支持拖拽移动和点击操作,是壁纸切换的快速入口。
- 支持鼠标拖拽移动窗口位置
- 拖拽阈值检测,区分点击和拖拽操作
- 实时更新窗口位置到主进程
关键代码片段:
const onMouseMove = (e) => {
if (!startPos.value || !windowStartPos.value) return
const deltaX = e.screenX - startPos.value.x
const deltaY = e.screenY - startPos.value.y
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
if (distance > moveThreshold || isDragging.value) {
isDragging.value = true
const newX = windowStartPos.value.x + deltaX
const newY = windowStartPos.value.y + deltaY
window.FBW.setWindowPosition('suspensionBall', { x: newX, y: newY })
}
}- 切换主窗口:点击悬浮球切换主窗口显示/隐藏
- 自动切换壁纸:启动/停止自动壁纸切换功能
- 设置同步:监听主进程设置数据更新
关键代码片段:
const onMouseUp = () => {
if (!isDragging.value) {
// 如果没有拖拽,则认为是点击事件
window.FBW.toggleMainWindow()
}
}
const onToolClick = async (funcName) => {
if (typeof window.FBW[funcName] === 'function') {
await window.FBW[funcName]()
}
}预览图片窗口提供独立的图片查看功能,支持图片列表浏览和单张图片查看。
- 独立窗口显示,支持窗口大小调整
- 自定义标题栏,支持窗口操作
- 图片查看组件集成
关键代码片段:
<template>
<div class="window-container">
<custom-title-bar :resize-window="true" window-name="viewImageWindow" />
<div class="window-container-inner">
<view-image ref="viewImageRef" :options="viewImageOptions" />
</div>
</div>
</template>- 监听主进程发送的图片数据
- 支持图片列表和当前索引传递
- 实时更新显示内容
关键代码片段:
const doView = (activeIndex = -1, list = []) => {
viewImageRef.value.view(activeIndex, list)
}
const onSendPostDataCallback = (event, data) => {
doView(data.activeIndex, data.list)
}
onBeforeMount(() => {
window.FBW.onSendPostData(onSendPostDataCallback)
})动态壁纸窗口用于播放视频作为桌面壁纸,支持多种视频控制功能。
- 支持本地视频文件播放
- 自动转换为
fbwtp://协议 URL - 支持 HTTP 和 HTTPS 视频源
关键代码片段:
const handleSetVideoSource = (event, source) => {
if (source) {
if (!source.startsWith('fbwtp://') && !source.startsWith('http')) {
const formattedPath = source.replace(/\\/g, '/')
videoSrc.value = `fbwtp://fbw/api/videos/get?filePath=${formattedPath}`
} else {
videoSrc.value = source
}
}
}- 静音控制:支持视频静音/取消静音
- 帧率控制:限制视频播放帧率,优化性能
- 缩放模式:支持 cover、contain 等缩放模式
- 亮度/对比度:实时调整视频显示效果
桌面探索/合集触发的全屏视频预览使用 ViewVideo.vue:
- 关闭按钮位于右上角,默认不可见(
opacity: 0) - 鼠标 移入视频区域 时显示;键盘 Tab 聚焦 时同样可见(无障碍)
- 内联卡片播放的静音按钮见上文「探索与合集浏览」
关键代码片段:
const controlFrameRate = (timestamp) => {
if (!videoRef.value) return
const frameInterval = 1000 / frameRate.value
if (timestamp - lastFrameTime >= frameInterval) {
if (videoRef.value.paused) {
videoRef.value.play()
}
lastFrameTime = timestamp
} else {
if (!videoRef.value.paused) {
videoRef.value.pause()
}
}
rafId = requestAnimationFrame(controlFrameRate)
}- 支持亮度、对比度滤镜调整
- 实时计算和应用 CSS 滤镜效果
- 响应式布局适配
关键代码片段:
const filterStyle = computed(() => {
return `brightness(${brightness.value / 100}) contrast(${contrast.value / 100})`
})律动壁纸窗口基于音频频谱数据渲染动态视觉效果,支持多种律动效果。
- 使用 Web Audio API 采集音频数据
- 自动检测虚拟声卡设备(VB-Audio、BlackHole、立体声混音)
- 支持音频频谱分析和数据提取
关键代码片段:
const init = async () => {
audioContext = new (window.AudioContext || window.webkitAudioContext)()
const devices = await navigator.mediaDevices.enumerateDevices()
const audioInputs = devices.filter((d) => d.kind === 'audioinput')
const virtualDevice = audioInputs.find(
(d) =>
d.label.includes('VB-Audio') ||
d.label.includes('BlackHole') ||
d.label.includes('立体声混音') ||
d.label.toLowerCase().includes('stereo mix')
)
if (virtualDevice) {
const stream = await navigator.mediaDevices.getUserMedia({
audio: { deviceId: virtualDevice.deviceId }
})
source = audioContext.createMediaStreamSource(stream)
analyser = audioContext.createAnalyser()
analyser.fftSize = 1024
dataArray = new Uint8Array(analyser.frequencyBinCount)
source.connect(analyser)
runEffect()
}
}当前为 4 种 Three.js 舞台效果(effects/three/stages/):
ThreeStageBars— 演唱会柱(默认)ThreeStageWall— 霓虹墙矩阵ThreeStageGrid— 地柱网格ThreeStageTexturedSphere— 纹理球
旧配置名(含已移除的 Leafer、ThreeStageMeshGrid 等)由 resolveRhythmEffect.js 映射到上述效果。
关键代码片段:
const effectName = resolveRhythmEffect(settingData.value.rhythmEffect)
const EffectClass = Effects[effectName]
effectInstance = new EffectClass(containerRef.value, toRaw(config.value))- 渲染:Three.js(
ThreeStageBase/StageEffectBase) - 音频:
createRhythmAudio+getFrame()驱动每帧更新 - 可配置:尺寸比例、配色、动画曲线、密度、位置、采样范围
ThreeStageBase:画布、相机、渲染循环StageEffectBase:舞台布局、图层、房间尺寸
详见 docs/rhythm_wallpaper.md。
- 使用 Pinia 进行状态管理
- 支持多窗口间的数据同步
- 与主进程 IPC 通信保持数据一致性
- 高度模块化的组件结构
- 可复用的自定义组件(如 CustomTitleBar、ViewImage、ViewInfo)
- 统一的组件注册和导入机制
- 基于 Element Plus 的响应式布局
- 支持窗口大小调整和内容自适应
- 流畅的过渡动画效果
- 集成 i18n 国际化框架
- 支持多语言界面切换
- 统一的文本管理机制