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

  1. Im VIS-Editor das entsprechende Projekt öffnen.
  2. Unter Projekt / Skripte (JavaScript) den bisherigen Skalierungs-Code vollständig durch den folgenden JavaScript-Code ersetzen. Nicht doppelt einfügen.
  3. Unter Projekt-CSS den bisherigen CSS-Code für dieses Homarr-Layout durch den nachfolgenden CSS-Code ersetzen.
  4. Änderungen speichern und Homarr vollständig neu laden.
  5. 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 Ansicht trash umbenannt 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, body und #vis_container beeinflusst 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.

Diese Website verwendet Cookies. Durch die Nutzung der Website stimmen Sie dem Speichern von Cookies auf Ihrem Computer zu. Außerdem bestätigen Sie, dass Sie unsere Datenschutzbestimmungen gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information