feat: add splash screen window

This commit is contained in:
Youwen Wu 2024-03-10 23:35:27 -07:00
parent 7317aa7afb
commit 49fe3cea9b
Signed by: youwen5
GPG key ID: 865658ED1FE61EC3
28 changed files with 2056 additions and 14 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,18 @@
use tauri::{Manager, Window};
// Create the command:
// This command must be async so that it doesn't run on the main thread.
#[tauri::command]
pub async fn close_splashscreen(window: Window) {
// Close splashscreen
window
.get_window("splashscreen")
.expect("no window labeled 'splashscreen' found")
.close()
.unwrap();
// Show main window
window
.get_window("main")
.expect("no window labeled 'main' found")
.show()
.unwrap();
}

View file

@ -4,6 +4,7 @@
use tauri::Manager; use tauri::Manager;
mod telemetry; mod telemetry;
use tracing_subscriber::FmtSubscriber; use tracing_subscriber::FmtSubscriber;
mod close_splashscreen;
#[derive(Clone, serde::Serialize)] #[derive(Clone, serde::Serialize)]
struct Payload { struct Payload {
@ -23,6 +24,13 @@ fn main() {
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber).unwrap();
rt.block_on(async { rt.block_on(async {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
close_splashscreen::close_splashscreen
])
.run(tauri::generate_context!())
.expect("failed to run app");
tauri::Builder::default() tauri::Builder::default()
.setup(|app| { .setup(|app| {
// create app handle and send it to our event listeners // create app handle and send it to our event listeners

View file

@ -62,7 +62,19 @@
"height": 600, "height": 600,
"resizable": true, "resizable": true,
"title": "Jankboard 2", "title": "Jankboard 2",
"width": 800 "width": 800,
"visible": false
},
{
"fullscreen": false,
"maximized": true,
"decorations": false,
"titleBarStyle": "Transparent",
"height": 600,
"resizable": true,
"width": 800,
"url": "splashscreen.html",
"label": "splashscreen"
} }
] ]
} }

View file

@ -15,7 +15,6 @@
let activeApp: App = 'camera' let activeApp: App = 'camera'
let loading = $settingsStore.fastStartup ? false : true
let unlistenAll: () => void let unlistenAll: () => void
onMount(() => { onMount(() => {
@ -28,10 +27,7 @@
initializeTelemetry().then((unsubFunction: () => void) => { initializeTelemetry().then((unsubFunction: () => void) => {
unlistenAll = unsubFunction unlistenAll = unsubFunction
}) })
setTimeout(() => { setTimeout(initializationSequence, 3000)
loading = false
initializationSequence()
}, 3000)
settingsStore.subscribe((value) => { settingsStore.subscribe((value) => {
localStorage.setItem('settings', JSON.stringify(value)) localStorage.setItem('settings', JSON.stringify(value))
@ -43,10 +39,7 @@
}) })
</script> </script>
<main <main class="select-none transition-opacity duration-300">
class="select-none transition-opacity duration-300"
class:opacity-0={loading}
>
<!-- driver dashboard --> <!-- driver dashboard -->
<div class="h-screen w-[35vw] fixed shadow-lg shadow-slate-800 z-10"> <div class="h-screen w-[35vw] fixed shadow-lg shadow-slate-800 z-10">
<Dashboard /> <Dashboard />
@ -63,10 +56,6 @@
</div> </div>
</main> </main>
{#if loading}
<Loading />
{/if}
<!-- toast service --> <!-- toast service -->
<Toaster /> <Toaster />

View file

@ -0,0 +1,5 @@
import { invoke } from '@tauri-apps/api/tauri'
export default async () => {
await invoke('hide_splashscreen')
}

24
splash-screen/.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

43
splash-screen/README.md Normal file
View file

@ -0,0 +1,43 @@
# Jankboard 2 Splash Screen
This directory contains the source code for the splash screen used by the Tauri
app. It's a standalone Svelte + Vite project, with its own separate
dependencies. In most cases, you won't ever have to touch the code in this
directory unless you want to modify the loading screen.
## How it works
Tauri allows you to add a splash screen to display before your app loads by
adding a file to the output directory called `splashscreen.html`. This is done
in Jankboard 2 by adding the `splashscreen.html` file to the `public` directory,
as recommended by Tauri's docs. However, since it's just one HTML file, we can't
typically use a framework like Svelte, which outputs a CSS and JS bundle. While
we can definitely write a simple splash screen in a raw HTML file, we miss out
on nice Svelte features like easy to write animations. The Vite project in this
directory solves that by using
[vite-plugin-singlefile](https://github.com/richardtallent/vite-plugin-singlefile)
to inline all CSS and JS outputted by Svelte's compiler into the HTML, resulting
in a nice single `index.html` output in `./dist`, which can then be renamed as
`splashscreen.html` and moved into the main Jankboard 2 project to be used as
the loading splash screen. The best part about this is that since Svelte is
meant to be compiled into such a small bundle size, there are virtually zero
downsides to doing this versus writing the splash screen in raw HTML. We can
easily write a nice looking splash screen using the same techniques as our
existing codebase without having to deal with raw HTML and CSS. If you don't
need to modify the splash screen, you **don't need to touch this project!**
`splashscreen.html` is already included in the main project and you don't have
to build it yourself here. If you do want to modify this splash screen, continue
to the instructions below.
## Installation and development instructions
**Again, you don't need to do this if you don't want to modify the splash
screen!**
1. Install dependencies via `pnpm install`.
2. Run `pnpm dev` to start the development server.
3. After you're done making your changes, run `pnpm build`. This will output a
single `index.html` file to `./dist`, which contains all of the JS and CSS
inlined.
4. Rename this outputted `index.html` to `splashscreen.html`, and then move it
into `/client/public`, replacing the existing `splashscreen.html`.

12
splash-screen/index.html Normal file
View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jankboard 2</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View file

@ -0,0 +1,25 @@
{
"name": "splash-screen",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@tsconfig/svelte": "^5.0.2",
"autoprefixer": "^10.4.18",
"postcss": "^8.4.35",
"svelte": "^4.2.11",
"svelte-check": "^3.6.4",
"tailwindcss": "^3.4.1",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"vite": "^5.1.4",
"vite-plugin-singlefile": "^2.0.1"
}
}

1678
splash-screen/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -0,0 +1,7 @@
<script lang="ts">
import svelteLogo from './assets/svelte.svg'
import Counter from './lib/Counter.svelte'
import Loading from './lib/Loading.svelte'
</script>
<Loading />

12
splash-screen/src/app.css Normal file
View file

@ -0,0 +1,12 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
overflow: hidden;
overscroll-behavior: none;
user-select: none;
background-image: url('./assets/background.jpg');
background-repeat: no-repeat;
background-size: cover;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,10 @@
<script lang="ts">
let count: number = 0
const increment = () => {
count += 1
}
</script>
<button on:click={increment}>
count is {count}
</button>

View file

@ -0,0 +1,22 @@
<script lang="ts">
import { blur } from 'svelte/transition'
import SvelteLogo from './SvelteLogo.svelte'
</script>
<div
class="absolute w-screen h-screen flex justify-center items-center flex-col overflow-hidden select-none bg"
transition:blur={{ duration: 300, amount: 0.5 }}
>
<div class="max-w-64">
<SvelteLogo />
</div>
</div>
<style lang="postcss">
.bg {
background-image: url('../../assets/wallpaper.jpg');
background-repeat: no-repeat;
background-size: cover;
}
</style>

View file

@ -0,0 +1,64 @@
<script lang="ts">
import { quintOut } from 'svelte/easing'
import { fade, draw, fly } from 'svelte/transition'
import { expand } from './customTransitions'
import { inner, outer } from './shape'
import { onMount } from 'svelte'
let visible = false
onMount(() => {
visible = true
})
</script>
{#if visible}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 103 124">
<g out:fade={{ duration: 200 }} opacity="0.35">
<path
in:expand={{ duration: 400, delay: 1000, easing: quintOut }}
style="stroke: #ff3e00; fill: #ff3e00; stroke-width: 50;"
d={outer}
/>
<path
in:draw={{ duration: 1000 }}
style="stroke:#ff3e00; stroke-width: 2;"
stroke-linecap="round"
d={inner}
/>
</g>
</svg>
<div class="centered" out:fly={{ y: -20, duration: 800 }}>
{#each 'JANKBOARD' as char, i}
<span in:fade|global={{ delay: 1000 + i * 150, duration: 800 }}
>{char}</span
>
{/each}
</div>
{/if}
<style lang="postcss">
svg {
width: 100%;
height: 100%;
}
path {
fill: white;
opacity: 1;
}
.centered {
@apply text-8xl absolute text-slate-300;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
letter-spacing: 0.12em;
font-weight: 400;
}
.centered span {
will-change: filter;
}
</style>

View file

@ -0,0 +1,18 @@
import { cubicOut } from 'svelte/easing'
import type { EasingFunction } from 'svelte/transition'
export function expand(
node: Element,
params: { delay: number; duration: number; easing: EasingFunction }
) {
const { delay = 0, duration = 400, easing = cubicOut } = params
const w = parseFloat(getComputedStyle(node).strokeWidth)
return {
delay,
duration,
easing,
css: (t: number) => `opacity: ${t}; stroke-width: ${t * w}`,
}
}

View file

@ -0,0 +1,2 @@
export const inner = `M45.41,108.86A21.81,21.81,0,0,1,22,100.18,20.2,20.2,0,0,1,18.53,84.9a19,19,0,0,1,.65-2.57l.52-1.58,1.41,1a35.32,35.32,0,0,0,10.75,5.37l1,.31-.1,1a6.2,6.2,0,0,0,1.11,4.08A6.57,6.57,0,0,0,41,95.19a6,6,0,0,0,1.68-.74L70.11,76.94a5.76,5.76,0,0,0,2.59-3.83,6.09,6.09,0,0,0-1-4.6,6.58,6.58,0,0,0-7.06-2.62,6.21,6.21,0,0,0-1.69.74L52.43,73.31a19.88,19.88,0,0,1-5.58,2.45,21.82,21.82,0,0,1-23.43-8.68A20.2,20.2,0,0,1,20,51.8a19,19,0,0,1,8.56-12.7L56,21.59a19.88,19.88,0,0,1,5.58-2.45A21.81,21.81,0,0,1,85,27.82,20.2,20.2,0,0,1,88.47,43.1a19,19,0,0,1-.65,2.57l-.52,1.58-1.41-1a35.32,35.32,0,0,0-10.75-5.37l-1-.31.1-1a6.2,6.2,0,0,0-1.11-4.08,6.57,6.57,0,0,0-7.06-2.62,6,6,0,0,0-1.68.74L36.89,51.06a5.71,5.71,0,0,0-2.58,3.83,6,6,0,0,0,1,4.6,6.58,6.58,0,0,0,7.06,2.62,6.21,6.21,0,0,0,1.69-.74l10.48-6.68a19.88,19.88,0,0,1,5.58-2.45,21.82,21.82,0,0,1,23.43,8.68A20.2,20.2,0,0,1,87,76.2a19,19,0,0,1-8.56,12.7L51,106.41a19.88,19.88,0,0,1-5.58,2.45`
export const outer = `M65,34 L37,52 A1 1 0 0 0 44 60 L70.5,44.5 A1 1 0 0 0 65,34Z M64,67 L36,85 A1 1 0 0 0 42 94 L68,77.5 A1 1 0 0 0 64,67Z`

View file

@ -0,0 +1,8 @@
import './app.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app'),
})
export default app

2
splash-screen/src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

View file

@ -0,0 +1,7 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: vitePreprocess(),
}

View file

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {},
},
plugins: [],
}

View file

@ -0,0 +1,20 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true
},
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View file

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true
},
"include": ["vite.config.ts"]
}

View file

@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { viteSingleFile } from 'vite-plugin-singlefile'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte(), viteSingleFile()],
})