Files
AeroMc-Launcher/src/fensteraufbau.js
T
2026-10-04 18:46:00 +00:00

86 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* "Fenster wachsen aus dem Knopf": Ein Popup-Fenster (Optionen, Mods, Skin,
* Freunde …) erscheint nicht schlagartig, sondern wächst aus dem angeklickten
* Knopf heraus. Dafür wird das noch unsichtbare Fenster abfotografiert und das
* Bild in einem durchsichtigen Hilfsfenster vom Knopf aus groß gezogen; am Ende
* übernimmt das echte Fenster an exakt derselben Stelle.
*
* Das Hilfsfenster entsteht jedes Mal neu und gleich in der richtigen Größe:
* Unter Windows färbt sich ein durchsichtiges Fenster schwarz, sobald es
* nachträglich vergrößert wird, und ein verstecktes Fenster zeichnet nicht mehr.
* Abschaltbar unter Optionen → Darstellung → Animationen („Bewegte Oberfläche“).
*/
const path = require('path');
const { BrowserWindow } = require('electron');
const DAUER_MS = 280;
const HOECHSTENS_MS = 2000; // spätestens dann ist das Fenster in jedem Fall zu sehen
const warte = (ms) => new Promise((r) => setTimeout(r, ms));
function vereinigung(a, b) {
const x = Math.min(a.x, b.x);
const y = Math.min(a.y, b.y);
return {
x, y,
width: Math.max(a.x + a.width, b.x + b.width) - x,
height: Math.max(a.y + a.height, b.y + b.height) - y,
};
}
// Rechteck in Koordinaten des Hilfsfensters umrechnen
const relativ = (r, box) => ({ x: r.x - box.x, y: r.y - box.y, width: r.width, height: r.height });
async function abspielen(ziel, von, merke) {
// unsichtbar zeigen, damit Chromium den Inhalt zeichnet, dann abfotografieren
ziel.setOpacity(0);
ziel.setIgnoreMouseEvents(true);
ziel.show();
await warte(90);
if (ziel.isDestroyed()) return;
const bild = await ziel.webContents.capturePage();
if (bild.isEmpty() || ziel.isDestroyed()) return;
const nach = ziel.getBounds();
const box = vereinigung(von, nach);
const hilfe = new BrowserWindow({
x: box.x, y: box.y, width: box.width, height: box.height,
show: false, frame: false, transparent: true, resizable: false, movable: false,
minimizable: false, maximizable: false, fullscreenable: false, focusable: false,
skipTaskbar: true, hasShadow: false, alwaysOnTop: true, backgroundColor: '#00000000',
title: 'AeroMC',
webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true },
});
merke(hilfe);
hilfe.setIgnoreMouseEvents(true);
await hilfe.loadFile(path.join(__dirname, 'aufbau.html'));
const ok = await hilfe.webContents.executeJavaScript(
`vorbereiten(${JSON.stringify(bild.toDataURL())}, ${JSON.stringify(relativ(nach, box))}, ${JSON.stringify(relativ(von, box))})`);
if (!ok || hilfe.isDestroyed()) return;
hilfe.showInactive();
await hilfe.webContents.executeJavaScript(`abspielen(${DAUER_MS})`);
}
/**
* Zeigt `ziel` (geladen, aber noch nicht sichtbar) mit Wachs-Animation an.
* `von` ist das Rechteck des angeklickten Knopfs in Bildschirmkoordinaten.
*/
async function wachsen(ziel, von) {
let hilfe = null;
try {
await Promise.race([abspielen(ziel, von, (h) => { hilfe = h; }), warte(HOECHSTENS_MS)]);
} catch {
// Animation ist nur Zierde – im Fehlerfall das Fenster einfach zeigen
} finally {
if (!ziel.isDestroyed()) {
ziel.setOpacity(1);
ziel.setIgnoreMouseEvents(false);
if (!ziel.isVisible()) ziel.show();
ziel.focus();
}
// Hilfsfenster erst nach dem Umschalten schließen, sonst blitzt der Hintergrund durch
setTimeout(() => { if (hilfe && !hilfe.isDestroyed()) hilfe.destroy(); }, 60);
}
}
module.exports = { wachsen };