86 lines
3.4 KiB
JavaScript
86 lines
3.4 KiB
JavaScript
/*
|
||
* "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 };
|