Fix: tauri window scaling bug

This commit is contained in:
DaZuo0122
2026-02-13 17:25:28 +08:00
parent 084506e84b
commit 8e79bd98e5
7 changed files with 80 additions and 5 deletions

View File

@@ -2,19 +2,60 @@ import React from "react";
import ReactDOM from "react-dom/client";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { LogicalPosition, LogicalSize, getCurrentWindow } from "@tauri-apps/api/window";
import { PixiPetRenderer, type UiSpritePack, type UiSnapshot } from "./renderer/pixi_pet";
import "./styles.css";
const WINDOW_PADDING = 16;
const MIN_WINDOW_SIZE = 64;
const SIZE_EPSILON = 0.5;
const SCALE_EPSILON = 0.0001;
function fittedWindowSize(
frameWidth: number,
frameHeight: number,
scale: number
): { width: number; height: number } {
const safeScale = Number.isFinite(scale) && scale > 0 ? scale : 1;
const width = Math.max(frameWidth * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE);
const height = Math.max(frameHeight * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE);
return { width, height };
}
async function fitWindowForScale(pack: UiSpritePack, scale: number): Promise<void> {
const window = getCurrentWindow();
const [outerPosition, innerSize] = await Promise.all([
window.outerPosition(),
window.innerSize()
]);
const target = fittedWindowSize(pack.frame_width, pack.frame_height, scale);
const widthChanged = Math.abs(target.width - innerSize.width) > SIZE_EPSILON;
const heightChanged = Math.abs(target.height - innerSize.height) > SIZE_EPSILON;
if (!widthChanged && !heightChanged) {
return;
}
const deltaWidth = target.width - innerSize.width;
const deltaHeight = target.height - innerSize.height;
const targetX = outerPosition.x - deltaWidth / 2;
const targetY = outerPosition.y - deltaHeight;
await window.setSize(new LogicalSize(target.width, target.height));
await window.setPosition(new LogicalPosition(targetX, targetY));
}
function App(): JSX.Element {
const [snapshot, setSnapshot] = React.useState<UiSnapshot | null>(null);
const [error, setError] = React.useState<string | null>(null);
const hostRef = React.useRef<HTMLDivElement | null>(null);
const rendererRef = React.useRef<PixiPetRenderer | null>(null);
const scaleFitRef = React.useRef<number | null>(null);
React.useEffect(() => {
let unlisten: null | (() => void) = null;
let mounted = true;
let activePack: UiSpritePack | null = null;
Promise.all([
invoke<UiSpritePack>("load_active_sprite_pack"),
invoke<UiSnapshot>("current_state")
@@ -23,6 +64,7 @@ function App(): JSX.Element {
if (!mounted) {
return;
}
activePack = pack;
setSnapshot(initialSnapshot);
if (hostRef.current !== null) {
rendererRef.current = await PixiPetRenderer.create(
@@ -31,6 +73,9 @@ function App(): JSX.Element {
initialSnapshot
);
}
scaleFitRef.current = initialSnapshot.scale;
await fitWindowForScale(pack, initialSnapshot.scale);
unlisten = await listen<UiSnapshot>("runtime:snapshot", (event) => {
if (!mounted) {
return;
@@ -38,6 +83,21 @@ function App(): JSX.Element {
const value = event.payload;
setSnapshot(value);
rendererRef.current?.applySnapshot(value);
if (activePack === null) {
return;
}
if (
scaleFitRef.current !== null &&
Math.abs(scaleFitRef.current - value.scale) < SCALE_EPSILON
) {
return;
}
scaleFitRef.current = value.scale;
void fitWindowForScale(activePack, value.scale).catch((err) => {
if (mounted) {
setError(String(err));
}
});
});
})
.catch((err) => {