Add: global factor controlling fps base interval

This commit is contained in:
DaZuo0122
2026-02-15 09:40:51 +08:00
parent e5417b6799
commit f20ed1fd9d
12 changed files with 289 additions and 52 deletions

View File

@@ -17,6 +17,7 @@ type UiSettingsSnapshot = {
scale: number;
visible: boolean;
always_on_top: boolean;
tauri_animation_slowdown_factor: number;
};
type UiSpritePackOption = {
@@ -33,6 +34,9 @@ const SCALE_MIN = 0.5;
const SCALE_MAX = 3.0;
const LOGICAL_BASE_FRAME_WIDTH = 512;
const LOGICAL_BASE_FRAME_HEIGHT = 512;
const SLOWDOWN_FACTOR_MIN = 1;
const SLOWDOWN_FACTOR_MAX = 20;
const SLOWDOWN_FACTOR_DEFAULT = 3;
async function invokeSetSpritePack(packIdOrPath: string): Promise<UiSnapshot> {
return invoke<UiSnapshot>("set_sprite_pack", { packIdOrPath });
@@ -50,6 +54,14 @@ async function invokeSetAlwaysOnTop(alwaysOnTop: boolean): Promise<UiSnapshot> {
return invoke<UiSnapshot>("set_always_on_top", { alwaysOnTop });
}
async function invokeAnimationSlowdownFactor(): Promise<number> {
return invoke<number>("tauri_animation_slowdown_factor");
}
async function invokeSetAnimationSlowdownFactor(factor: number): Promise<number> {
return invoke<number>("set_tauri_animation_slowdown_factor", { factor });
}
function fittedWindowSize(
scale: number
): { width: number; height: number } {
@@ -142,6 +154,7 @@ function MainOverlayWindow(): JSX.Element {
const activePackRef = React.useRef<UiSpritePack | null>(null);
const loadedPackKeyRef = React.useRef<string | null>(null);
const effectiveScaleSyncRef = React.useRef<number | null>(null);
const slowdownFactorRef = React.useRef<number>(SLOWDOWN_FACTOR_DEFAULT);
const loadingPackRef = React.useRef(false);
const mountedRef = React.useRef(false);
@@ -158,6 +171,7 @@ function MainOverlayWindow(): JSX.Element {
}
const previousRenderer = rendererRef.current;
const nextRenderer = await PixiPetRenderer.create(hostRef.current, pack, nextSnapshot);
nextRenderer.setAnimationSlowdownFactor(slowdownFactorRef.current);
rendererRef.current = nextRenderer;
activePackRef.current = pack;
loadedPackKeyRef.current = nextSnapshot.active_sprite_pack;
@@ -252,13 +266,18 @@ function MainOverlayWindow(): JSX.Element {
Promise.all([
invoke<UiSpritePack>("load_active_sprite_pack"),
invoke<UiSnapshot>("current_state"),
invoke<boolean>("debug_overlay_visible")
invoke<boolean>("debug_overlay_visible"),
invokeAnimationSlowdownFactor()
])
.then(async ([pack, initialSnapshot, showDebug]) => {
.then(async ([pack, initialSnapshot, showDebug, slowdownFactor]) => {
if (!mountedRef.current) {
return;
}
setDebugOverlayVisible(showDebug);
slowdownFactorRef.current = Math.min(
Math.max(Math.round(slowdownFactor), SLOWDOWN_FACTOR_MIN),
SLOWDOWN_FACTOR_MAX
);
await recreateRenderer(pack, initialSnapshot);
await processSnapshot(initialSnapshot);
@@ -271,9 +290,23 @@ function MainOverlayWindow(): JSX.Element {
}
setDebugOverlayVisible(Boolean(event.payload));
});
const unlistenSlowdown = await listen<number>(
"runtime:animation-slowdown-factor",
(event) => {
if (!mountedRef.current) {
return;
}
slowdownFactorRef.current = Math.min(
Math.max(Math.round(Number(event.payload)), SLOWDOWN_FACTOR_MIN),
SLOWDOWN_FACTOR_MAX
);
rendererRef.current?.setAnimationSlowdownFactor(slowdownFactorRef.current);
}
);
unlisten = () => {
unlistenSnapshot();
unlistenDebug();
unlistenSlowdown();
};
})
.catch((err) => {
@@ -406,10 +439,29 @@ function SettingsWindow(): JSX.Element {
active_sprite_pack: payload.active_sprite_pack,
scale: payload.scale,
visible: payload.visible,
always_on_top: payload.always_on_top
always_on_top: payload.always_on_top,
tauri_animation_slowdown_factor:
prev.tauri_animation_slowdown_factor ?? SLOWDOWN_FACTOR_DEFAULT
};
});
});
const unlistenSlowdown = await listen<number>("runtime:animation-slowdown-factor", (event) => {
if (!mounted) {
return;
}
const factor = Math.min(
Math.max(Math.round(Number(event.payload)), SLOWDOWN_FACTOR_MIN),
SLOWDOWN_FACTOR_MAX
);
setSettings((prev) =>
prev === null ? prev : { ...prev, tauri_animation_slowdown_factor: factor }
);
});
const previousUnlisten = unlisten;
unlisten = () => {
previousUnlisten();
unlistenSlowdown();
};
})
.catch((err) => {
if (mounted) {
@@ -499,6 +551,29 @@ function SettingsWindow(): JSX.Element {
[withPending]
);
const onAnimationSlowdownFactorChange = React.useCallback(
async (event: React.ChangeEvent<HTMLInputElement>) => {
const value = Number(event.target.value);
if (!Number.isFinite(value)) {
return;
}
const clamped = Math.min(
Math.max(Math.round(value), SLOWDOWN_FACTOR_MIN),
SLOWDOWN_FACTOR_MAX
);
const persisted = await withPending(() => invokeSetAnimationSlowdownFactor(clamped));
if (persisted === null) {
return;
}
setSettings((prev) =>
prev === null
? prev
: { ...prev, tauri_animation_slowdown_factor: Number(persisted) }
);
},
[withPending]
);
return (
<main className="settings-root">
<section className="settings-card">
@@ -535,6 +610,20 @@ function SettingsWindow(): JSX.Element {
onChange={onScaleChange}
/>
</label>
<label className="field">
<span>
Animation Speed Factor: x{settings.tauri_animation_slowdown_factor}
</span>
<input
type="range"
min={SLOWDOWN_FACTOR_MIN}
max={SLOWDOWN_FACTOR_MAX}
step={1}
value={settings.tauri_animation_slowdown_factor}
disabled={pending}
onChange={onAnimationSlowdownFactorChange}
/>
</label>
<label className="toggle">
<input
type="checkbox"