关于 adding-tauri-splashscreen
面向 Tauri v2 应用的启动画面实现指南,涵盖窗口配置以及前后端初始化协调,待准备完成后再显示主窗口。
主要功能
- 01配置启动画面和主窗口
- 02协调前后端启动流程
- 03关闭启动画面并显示主窗口
- 04设计简约、进度条和深色画面
- 05处理初始化错误和显示时机
适用场景
- 01在 Tauri 应用初始化时显示加载画面
- 02初始化完成前隐藏主窗口
- 03排查启动画面的显示和位置问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dchuk/ claude-code-tauri-skills --skill adding-tauri-splashscreen安装后在 Claude Code 中输入/adding-tauri-splashscreen确认已加载。
详细介绍
Tauri 启动画面实现
本技能介绍如何在 Tauri v2 应用中实现启动画面。应用启动时,主窗口加载和初始化期间会显示启动画面。
概述
启动画面的实现模式包括:
- 启动后立即显示启动窗口
- 主窗口就绪前将其隐藏
- 执行初始化任务(前端和后端)
- 初始化完成后关闭启动画面并显示主窗口
配置
窗口配置
在 tauri.conf.json 中配置这两个窗口:
{
"app": {
"windows": [
{
"label": "main",
"title": "My Application",
"width": 1200,
"height": 800,
"visible": false
},
{
"label": "splashscreen",
"title": "Loading",
"url": "splashscreen.html",
"width": 400,
"height": 300,
"center": true,
"resizable": false,
"decorations": false,
"transparent": true,
"alwaysOnTop": true
}
]
}
}
关键设置:
- 主窗口设置
"visible": false,让它在就绪前保持隐藏 "url": "splashscreen.html",指向启动画面 HTML"decorations": false,移除窗口边框,使界面更简洁"transparent": true,启用透明背景"alwaysOnTop": true,确保加载期间启动画面始终可见
启动画面 HTML
在前端源代码目录(例如 src/ 或 public/)中创建 splashscreen.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
overflow: hidden;
background: transparent;
}
.splash-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
color: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.logo {
width: 80px;
height: 80px;
margin-bottom: 24px;
}
.app-name {
font-size: 24px;
font-weight: 600;
margin-bottom: 16px;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top-color: #4f46e5;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
margin-top: 16px;
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
}
</style>
</head>
<body>
<div class="splash-container">
<svg class="logo" viewBox="0 0 100 100" fill="none">
<circle cx="50" cy="50" r="45" stroke="#4f46e5" stroke-width="4"/>
<path d="M30 50 L45 65 L70 35" stroke="#4f46e5" stroke-width="4" fill="none"/>
</svg>
<div class="app-name">My Application</div>
<div class="loading-spinner"></div>
<div class="loading-text">Loading...</div>
</div>
</body>
</html>
前端设置
TypeScript/JavaScript 实现
在主入口文件(例如 src/main.ts)中:
import { invoke } from '@tauri-apps/api/core';
// Helper function for delays
function sleep(seconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
// Frontend initialization
async function initializeFrontend(): Promise<void> {
// Perform frontend setup tasks here:
// - Load configuration
// - Initialize state management
// - Set up routing
// - Preload critical assets
// Example: simulate initialization time
await sleep(1);
// Notify backend that frontend is ready
await invoke('set_complete', { task: 'frontend' });
}
// Start initialization when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
initializeFrontend().catch(console.error);
});
替代方案:使用窗口事件
import { invoke } from '@tauri-apps/api/core';
import { getCurrentWindow } from '@tauri-apps/api/window';
async function initializeFrontend(): Promise<void> {
// Your initialization logic
const config = await loadConfig();
await setupRouter();
await preloadAssets();
// Signal completion
await invoke('set_complete', { task: 'frontend' });
}
// Wait for window to be fully ready
getCurrentWindow().once('tauri://created', () => {
initializeFrontend();
});
后端设置
添加 Tokio 依赖
cargo add tokio --features time
Rust 实现
在 src-tauri/src/lib.rs 中:
use std::sync::Mutex;
use tauri::{AppHandle, Manager, State};
// Track initialization state
struct SetupState {
frontend_task: bool,
backend_task: bool,
}
impl Default for SetupState {
fn default() -> Self {
Self {
frontend_task: false,
backend_task: false,
}
}
}
// Command to mark tasks complete
#[tauri::command]
async fn set_complete(
app: AppHandle,
state: State<'_, Mutex<SetupState>>,
task: String,
) -> Result<(), String> {
let mut state = state.lock().map_err(|e| e.to_string())?;
match task.as_str() {
"frontend" => state.frontend_task = true,
"backend" => state.backend_task = true,
_ => return Err(format!("Unknown task: {}", task)),
}
// Check if all tasks are complete
if state.frontend_task && state.backend_task {
// Close splash and show main window
if let Some(splash) = app.get_webview_window("splashscreen") {
splash.close().map_err(|e| e.to_string())?;
}
if let Some(main) = app.get_webview_window("main") {
main.show().map_err(|e| e.to_string())?;
main.set_focus().map_err(|e| e.to_string())?;
}
}
Ok(())
}
// Backend initialization
async fn setup_backend(app: AppHandle) {
// IMPORTANT: Use tokio::time::sleep, NOT std::thread::sleep
// std::thread::sleep blocks the entire async runtime
// Perform backend initialization:
// - Database connections
// - Load configuration
// - Initialize services
// Example: simulate work
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
// Mark backend as complete
if let Some(state) = app.try_state::<Mutex<SetupState>>() {
let _ = set_complete(
app.clone(),
state,
"backend".to_string(),
).await;
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(Mutex::new(SetupState::default()))
.invoke_handler(tauri::generate_handler![set_complete])
.setup(|app| {
let handle = app.handle().clone();
// Spawn backend initialization
tauri::async_runtime::spawn(async move {
setup_backend(handle).await;
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
简单实现
对于只需等待前端就绪的简单情况:
配置
{
"app": {
"windows": [
{
"label": "main",
"visible": false
},
{
"label": "splashscreen",
"url": "splashscreen.html",
"width": 400,
"height": 300,
"decorations": false
}
]
}
}
前端
import { invoke } from '@tauri-apps/api/core';
async function init() {
// Initialize your app
await setupApp();
// Close splash, show main
await invoke('close_splashscreen');
}
document.addEventListener('DOMContentLoaded', init);
后端
use tauri::{AppHandle, Manager};
#[tauri::command]
async fn close_splashscreen(app: AppHandle) -> Result<(), String> {
if let Some(splash) = app.get_webview_window("splashscreen") {
splash.close().map_err(|e| e.to_string())?;
}
if let Some(main) = app.get_webview_window("main") {
main.show().map_err(|e| e.to_string())?;
main.set_focus().map_err(|e| e.to_string())?;
}
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![close_splashscreen])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
样式变体
极简启动画面
<style>
.splash-container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: #ffffff;
}
.logo {
width: 120px;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(0.95); }
}
</style>
进度条启动画面
<style>
.progress-container {
width: 200px;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
margin-top: 20px;
}
.progress-bar {
height: 100%;
background: #4f46e5;
animation: progress 2s ease-in-out infinite;
}
@keyframes progress {
0% { width: 0%; }
50% { width: 70%; }
100% { width: 100%; }
}
</style>
<div class="progress-container">
<div class="progress-bar"></div>
</div>
带光晕的深色主题
<style>
.splash-container {
background: #0a0a0a;
color: #ffffff;
}
.logo {
filter: drop-shadow(0 0 20px rgba(79, 70, 229, 0.5));
}
.app-name {
text-shadow: 0 0 20px rgba(79, 70, 229, 0.5);
}
</style>
重要说明
-
异步休眠:在异步 Rust 代码中始终使用
tokio::time::sleep,不要使用std::thread::sleep。后者会阻塞整个运行时。 -
窗口标签:确保代码中的窗口标签与
tauri.conf.json中的标签一致。 -
错误处理:启动画面应能妥善处理错误。如果初始化失败,仍应显示主窗口,并呈现错误状态。
-
显示时长:启动画面应保持足够长的显示时间以展示品牌,但不要久到让用户感到厌烦。建议至少显示 1-3 秒。
-
透明窗口:使用
transparent: true时,确保 HTML 中html和body元素的背景设为background: transparent。 -
移动端注意事项:启动画面在移动平台上的工作方式有所不同。考虑为 iOS 和 Android 使用平台原生启动画面。
故障排除
启动画面未显示:
- 确认
tauri.conf.json中的 URL 路径正确 - 检查 HTML 文件是否位于正确位置
主窗口过早显示:
- 确保主窗口设置了
visible: false - 确认
set_complete命令已正确调用
透明背景无效:
- 在窗口配置中设置
transparent: true - 在 CSS 中将 html 和 body 的背景设为
background: transparent - 在某些平台上,可能还需要设置
decorations: false
窗口位置问题:
- 使用
center: true将启动画面居中 - 或指定明确的
x和y坐标
评论
支持 Markdown