Metainformationen zur Seite
ioBroker VIS in Homarr – Skalierung, Zentrierung und HiDPI
Zweck
Die VIS-Ansicht trash wird als iFrame in Homarr dargestellt. Beim Verändern der Homarr-Kachel werden alle Widgets gemeinsam proportional skaliert und horizontal/vertikal zentriert. Canvas-Gauges werden mit höherer interner Auflösung gezeichnet, damit sie auf Monitoren mit unterschiedlicher Pixeldichte scharf bleiben.
| Einstellung | Wert / Funktion |
|---|---|
| VIS-Ansicht | trash |
| VIS-Auflösung | 400 × 480 px (wird aus VIS gelesen) |
| Hintergrund | #25262b |
| Max. HiDPI-Faktor | 4 |
| Startbedingung | Nur im iFrame, nicht im VIS-Editor |
| Anpassungen bei neuen Widgets | Keine JavaScript-Widget-IDs notwendig |
Einrichtung
- Im VIS-Editor das entsprechende Projekt öffnen.
- Unter Projekt / Skripte (JavaScript) den bisherigen Skalierungs-Code vollständig durch den folgenden JavaScript-Code ersetzen. Nicht doppelt einfügen.
- Unter Projekt-CSS den bisherigen CSS-Code für dieses Homarr-Layout durch den nachfolgenden CSS-Code ersetzen.
- Änderungen speichern und Homarr vollständig neu laden.
- Homarr-Kachel kleiner/größer ziehen und Darstellung prüfen.
JavaScript – automatische Skalierung, Zentrierung und scharfe Gauges
/** * ioBroker VIS (Ansicht "trash") in Homarr * -------------------------------------- * - Gesamte VIS proportional an die iFrame-Größe anpassen. * - Inhalt horizontal und vertikal zentrieren. * - Alle Canvas-Gauges automatisch in höherer Auflösung zeichnen. * - Keine festen Widget-IDs; neu hinzugefügte Gauges werden mit erfasst. * * Einbau: VIS-Editor -> Projekt -> Skripte / JavaScript. * Die Funktion läuft bewusst nur in einem iFrame, nicht im VIS-Editor. */ (function () { // Nicht in einem eigenständigen Browserfenster oder im VIS-Editor ausführen. if (window.self === window.top) return; if (window.vis && window.vis.editMode) return; // Verhindert eine doppelte Registrierung der Beobachter bei erneutem Laden. if (window.trashAutoScaleActive) return; window.trashAutoScaleActive = true; // Name der VIS-Ansicht. Nur bei Umbenennung der Ansicht ändern. const VIEW_NAME = "trash"; // Ersatzwerte, falls die VIS-Einstellungen noch nicht verfügbar sind. // Aktuelle Ansichtsgröße: 400 x 480 Pixel (wird normalerweise automatisch gelesen). const FALLBACK_WIDTH = 400; const FALLBACK_HEIGHT = 480; // HiDPI: höhere Werte = schärfer, aber größerer Grafik-/Speicherbedarf. const MAX_PIXEL_RATIO = 4; let updatePending = false; // Begrenzt Layout-Berechnungen auf einen pro Frame. let canvasClass = null; // Canvas-Klasse der geladenen Gauge-Bibliothek. let currentPixelRatio = 1; // Tatsächlich verwendeter HiDPI-Faktor. let redrawTimer = null; // Entprellt das Neuzeichnen bei Größenänderungen. /** * Erhöht die interne Zeichenauflösung der Canvas-Gauges. * Die sichtbaren Abmessungen der Widgets bleiben dabei unverändert. * * Wichtig: Die Bibliothek stellt "SmartCanvas" im Browser nicht zwingend * als window.SmartCanvas bereit. Deshalb ermitteln wir die Klasse über * das bereits erzeugte Gauge-Objekt eines beliebigen Canvas-Widgets. * Danach gilt der neue Faktor automatisch für alle Gauges dieser Klasse. */ function updateCanvasResolution(scale) { let Canvas = canvasClass; // Bibliotheksklasse einmalig über ein vorhandenes Gauge finden. if (!Canvas) { const view = document.getElementById("visview_" + VIEW_NAME); if (!view || !window.jQuery) return; const canvases = view.querySelectorAll("canvas.vis-canvas-gauges"); for (const element of canvases) { const widget = element.closest(".vis-widget"); if (!widget) continue; const gauge = window.jQuery(widget).data("gauge"); if ( gauge && gauge.canvas && typeof gauge.canvas.constructor.redraw === "function" ) { Canvas = gauge.canvas.constructor; break; } } } // Gauges noch nicht geladen: Beim nächsten Layout-Update erneut prüfen. if (!Canvas) return; // Bildschirm-Pixeldichte + Vergrößerung der gesamten VIS berücksichtigen. // Untergrenze 3x; Obergrenze 4x gegen unnötigen Ressourcenverbrauch. const dpr = window.devicePixelRatio || 1; const desired = Math.min( MAX_PIXEL_RATIO, Math.max(3, dpr * Math.max(1, scale) * 1.25) ); const nextRatio = Math.ceil(desired * 4) / 4; let installedNow = false; // Den zentralen PixelRatio-Getter der tatsächlichen Klasse einmal ersetzen. if (canvasClass !== Canvas) { const descriptor = Object.getOwnPropertyDescriptor(Canvas, "pixelRatio"); if (!descriptor || !descriptor.configurable) { console.warn("Canvas-Gauges: Pixeldichte nicht anpassbar"); return; } try { Object.defineProperty(Canvas, "pixelRatio", { configurable: true, get: function () { return currentPixelRatio; } }); canvasClass = Canvas; installedNow = true; } catch (error) { console.warn("Canvas-Gauges: HiDPI-Aktivierung fehlgeschlagen", error); return; } } // Ohne Änderung der benötigten Pixeldichte nicht erneut zeichnen. if ( !installedNow && Math.abs(nextRatio - currentPixelRatio) < 0.01 ) { return; } currentPixelRatio = nextRatio; // Kurze Verzögerung vermeidet viele Redraws beim Ziehen der Homarr-Kachel. clearTimeout(redrawTimer); redrawTimer = setTimeout(function () { try { canvasClass.redraw(); console.info("VIS Canvas-Gauges HiDPI:", currentPixelRatio + "x"); } catch (error) { console.warn("Canvas-Gauges: Neuzeichnen fehlgeschlagen", error); } }, 220); } /** * Passt die ganze VIS proportional in die aktuelle iFrame-Fläche ein. * Die Ansicht behält ihr Seitenverhältnis und wird in beiden Achsen zentriert. */ function scaleView() { updatePending = false; const container = document.getElementById("vis_container"); const view = document.getElementById("visview_" + VIEW_NAME); if (!container || !view) return; // Größe des tatsächlich verfügbaren VIS-Containers (Homarr-iFrame). const width = container.clientWidth; const height = container.clientHeight; if (width <= 0 || height <= 0) return; // Originalgröße direkt aus den VIS-Ansichtseinstellungen beziehen. // Bei einer späteren Änderung der VIS-Auflösung keine Code-Anpassung nötig. const settings = window.vis && window.vis.views && window.vis.views[VIEW_NAME] && window.vis.views[VIEW_NAME].settings; const baseWidth = parseInt(settings && settings.sizex, 10) || FALLBACK_WIDTH; const baseHeight = parseInt(settings && settings.sizey, 10) || FALLBACK_HEIGHT; // "contain": gesamte VIS sichtbar halten, niemals verzerren. const scale = Math.min(width / baseWidth, height / baseHeight); // Freie Fläche links/rechts und oben/unten gleichmäßig verteilen. const offsetX = (width - baseWidth * scale) / 2; const offsetY = (height - baseHeight * scale) / 2; // Ausgangsposition und Ausgangsabmessungen explizit setzen. view.style.position = "absolute"; view.style.left = "0px"; view.style.top = "0px"; view.style.width = baseWidth + "px"; view.style.height = baseHeight + "px"; // VIS hat standardmäßig min-width/min-height: 100 %. // Das würde unsere Größenberechnung beim Verkleinern verfälschen. view.style.minWidth = "0px"; view.style.minHeight = "0px"; // Zentrierung und Skalierung in einer Transformation kombinieren. view.style.transformOrigin = "top left"; view.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; // Den Canvas-Gauges ausreichend interne Bildpunkte geben. updateCanvasResolution(scale); } // Mehrere unmittelbar aufeinanderfolgende Änderungen zusammenfassen. function requestUpdate() { if (updatePending) return; updatePending = true; requestAnimationFrame(scaleView); } function initialize() { const container = document.getElementById("vis_container"); if (!container) return; // Homarr-iFrame/Browserfenster wird größer oder kleiner. window.addEventListener("resize", requestUpdate); // VIS-Container ändert sich, auch ohne klassisches window.resize. if (typeof ResizeObserver !== "undefined") { new ResizeObserver(requestUpdate).observe(container); } // Widgets werden beim VIS-Start ggf. verzögert eingefügt. // So finden wir die Gauge-Bibliothek auch nach dem ersten Rendern. new MutationObserver(requestUpdate).observe(container, { childList: true, subtree: true }); requestUpdate(); } // Erst starten, wenn der HTML-Dokumentbaum verfügbar ist. if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initialize, { once: true }); } else { initialize(); } })();
Projekt-CSS – Hintergrund und Trashschedule
/* ============================================================ ioBroker VIS "trash" in Homarr – Projekt-CSS ============================================================ */ /* 1. Seitenränder entfernen: Der iFrame-Inhalt beginnt ohne Abstand. */ html, body, .mainWrapper { margin: 0 !important; padding: 0 !important; } /* 2. Überlauf ausblenden: Keine Scrollbalken im Homarr-iFrame. Achtung: html/body wirken auf ALLE Ansichten dieses VIS-Projekts. */ html, body, .mainWrapper, #vis_container, .no-scrollbars { overflow: hidden !important; } /* 3. Einheitlicher Hintergrund auf allen Ebenen. Verhindert weiße Streifen bei wechselnder iFrame-Größe. Achtung: html/body und #vis_container wirken projektweit. */ html, body, .mainWrapper, #vis_container, #visview_trash { background-color: #25262b !important; } /* 4. Trashschedule: Mülltonnen trotz schmalem Rahmen nebeneinander. Selektor über Widget-Klasse statt ID: auch für weitere Trashschedule-Widgets innerhalb der Ansicht "trash". */ #visview_trash .trashschedule-widget .trashtypes { white-space: nowrap !important; width: max-content !important; transform-origin: top left !important; } /* 5. Weniger Abstand: Tonnen kompakter nebeneinander anordnen. */ #visview_trash .trashschedule-widget .trashtype { margin-left: 5px !important; margin-top: 0 !important; } /* Kein zusätzliches CSS für Canvas-Gauges notwendig: Die höhere Zeichenauflösung regelt das JavaScript. */
Funktionsprinzip
- Skalierung: Die VIS passt mit einem einheitlichen Faktor vollständig in den iFrame (Seitenverhältnis bleibt erhalten).
- Zentrierung: Nicht genutzte Breite und Höhe werden auf beide Seiten verteilt.
- HiDPI: Die Canvas-Gauges zeichnen intern mit höherer Pixeldichte; sichtbare Widgetmaße bleiben gleich.
- Neue Widgets: Die VIS-Gesamtskalierung gilt automatisch. Weitere Widgets aus derselben Canvas-Gauges-Bibliothek profitieren ebenfalls von der HiDPI-Einstellung.
- Mülltonnen: CSS verhindert Zeilenumbrüche und reduziert die Zwischenräume.
- Hintergrund: Alle relevanten VIS-Ebenen verwenden
#25262b, damit keine weißen Flächen erscheinen.
Anpassungen und Hinweise
VIEW_NAME: Nur ändern, wenn die Ansichttrashumbenannt wird.FALLBACK_WIDTH/FALLBACK_HEIGHT: Ersatzwerte, falls die gespeicherte VIS-Auflösung nicht rechtzeitig verfügbar ist. Im Normalfall wird die Ansichtsgröße automatisch ausgelesen.MAX_PIXEL_RATIO: Obergrenze für die Canvas-Zeichenauflösung. Größere Werte benötigen mehr Grafikleistung und Speicher.- Projektweit: CSS auf
html,bodyund#vis_containerbeeinflusst auch andere Ansichten desselben VIS-Projekts. Bei scrollbaren Ansichten gegebenenfalls getrennt konfigurieren. - Grenze: Die HiDPI-Funktion ist auf die verwendete
vis-canvas-gauges-Bibliothek abgestimmt; bei einem größeren Bibliotheksupdate kann eine Überprüfung erforderlich werden. Andere Canvas-Widgets werden nicht automatisch in HiDPI gezeichnet. - Fehlersuche: Im Browser (Entwicklerkonsole des VIS-iFrames) erscheint bei erfolgreichem HiDPI-Neuzeichnen z. B.
VIS Canvas-Gauges HiDPI: 3x. Falls keine Meldung erscheint, prüfen, ob Gauges bereits geladen sind und ob die Browserkonsole Fehler zeigt.
Wartung
Vor größeren Änderungen VIS-Projekt exportieren/sichern. Beim Hinzufügen normaler Widgets ist keine Anpassung des JavaScripts erforderlich; nur bei einer geänderten Ansichtsnamen oder abweichendem Widget-Typ die Konfiguration prüfen.