Nico R.: background-image transitionieren

problematische Seite

Hallo zusammen,

ich versuche ein background-image per JS auszutauschen und per CSS sanft zu überblenden. Der grundsätzliche Austausch funktioniert, aber transition scheint auf background-image nicht zu wirken. Mit background-color funktioniert es.

Kann ich mir das grundsätzlich abschminken? Ich meine, unlängst irgendwo einen Workaround gesehen zu haben, finde den aber nicht mehr.

Hier der Test. Wenn man herunter scrollt, wechselt die background-color:

https://fsv-optik.de/test_bg.html

Schöne Grüße

Nico

  1. problematische Seite

    Hi,

    ich versuche ein background-image per JS auszutauschen und per CSS sanft zu überblenden. Der grundsätzliche Austausch funktioniert, aber transition scheint auf background-image nicht zu wirken. Mit background-color funktioniert es.

    Kann ich mir das grundsätzlich abschminken? Ich meine, unlängst irgendwo einen Workaround gesehen zu haben, finde den aber nicht mehr.

    da waren beide Bilder übereinandergelegt, das eine transparent, das andere opak. Und dann wurde die Transparenz/Opazität gegenläufig animiert.

    cu,
    Andreas a/k/a MudGuard

    1. problematische Seite

      Hallo Andreas,

      meinst du innerhalb der CCS-Klasse übereinanderpacken? Oder eine Ebene darüber legen?

      Das zweitere hatte ich versucht. Da hatte ich natürlich das Problem, dass ich ja das background-image vom body überblenden will. Wenn ich da eine Ebene drüber lege, die ich auf opacity:0 setze, dann wird der komplette Inhalt unsichtbar. Das kann ich zwar mit display:contents verhindern, aber spätestens das Umschalten auf display:block ist ja auch wieder nicht animierbar.

      Schöne Grüße

      Nico

  2. problematische Seite

    Hallo Nico R.,

    du kannst Transitionen auf Eigenschaften anwenden, die einen kontinuierlichen Wertebereich haben. Das sind zum einen Zahlen, und für Farben kriegt der Browser das auch hin.

    Schlüsselworteigenschaften oder Bilder sind nicht kontinuierlich.

    Eine Überblendung zwischen zwei Bildern geht nur per Transparenz, allerdings ist so etwas wie "background-opacity" nicht vorhanden.

    Es mag Tricks geben, die ich nicht kenne; aber mein Ansatz wäre, das Element und zwei Divs übereinanderzustapeln und in den beiden Divs die beiden Hintergründe zu setzen. Diese beiden Divs können dann gegenläufig zwischen Transparent und Opak überblendet werden.

    Wie man das genau tut, hängt von deiner Anforderung ab. Sehe ich das richtig, dass Du das Bild komplett umblenden willst, sobald man vom oberen Rand wegscrollt? Oder hängt der Grad der Überblendung von der Scrollposition ab? Was ist, wenn man zum oberen Rand zurückkehrt?

    Rolf

    --
    sumpsi - posui - obstruxi
    1. problematische Seite

      Hallo Rolf,

      ja, genau. Wenn window.pageYOffset > Wert umblenden, wenn kleiner wieder zurückblenden.

      Wenn ich zwei DIVs mit verschiendenen background-images übereinander lege und die gegeneinander überblende, heißt das ja, dass eins davon am Anfang unsichtbar bzw. beim Überblenden nur zum Teil sichtbar ist. Und damit sind ja auch alle Kindelement, also der komplette Inhalt unsichtbar.

      Oder verstehe ich die Idee falsch? Ich habe den Eindruck.

      Gruß Nico

    2. problematische Seite

      Hi,

      Es mag Tricks geben, die ich nicht kenne; aber mein Ansatz wäre, das Element und zwei Divs übereinanderzustapeln

      sollten nicht auch Pseudo-Elemente (::before und ::after, auf dieselbe Größe und Position, aber z-index-mäßig hinter das eigentliche Element gelegt) ausreichen? Diese mit jeweils einem Hintergrundbild versehen, und dann die Opacity der Pseudo-Elemente animieren.

      Das würde Zusatz-Elemente im HTML vermeiden.

      (ungetestet, nur als Idee)

      cu,
      Andreas a/k/a MudGuard

      1. problematische Seite

        Hallo MudGuard,

        so was?

        Ich hab's bewusst einfach gehalten.

        Verwendet man für before/after die Einstellung position:fixed, bleibt das Bild unbeweglich und blendet nur über.

        Soll das Bild mitscrollen, muss man position:absolute verwenden, aber dann hat man mit der Höhe des Hintergrundbildes ein Problem - man kann es nicht an die Höhe des Body anpassen (zumindest kriege ich es nicht hin). Das ginge vielleicht besser, wenn man aus dem Body ein Grid macht, aber dann geht's nicht mit before/after, weil das keine Grid-Items sein können.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. problematische Seite

          Hi,

          so was?

          ja, so in der Art.

          War bei mir nur ne Idee.

          Verwendet man für before/after die Einstellung position:fixed, bleibt das Bild unbeweglich und blendet nur über.

          ok - wie gesagt, ich hab das nicht bis zum bitteren Ende durchdacht, war nur ein Ansatz. Ich selber brauch sowas derzeit nicht, also kein tieferes Beschäftigen damit meinerseits.

          cu,
          Andreas a/k/a MudGuard

        2. problematische Seite

          Wunderbar, das funktioniert! 😀 Bei mir ist das background-image sowie fixed, von daher passt das genau.

          Ich hab dabei gleich noch etwas über Pseudoelemente (speziell before:: und after::) gelernt, die waren mir bisher immer suspekt und ich hab sie meist gemieden. Aber vielleicht hab ich ihnen unrecht getan ;-) Und .toggle() kannte ich auch noch nicht bzw. nur aus jQuery.

          Sehr schön, tausend Dank euch beiden und einen schönen Abend

          Nico

          P.S.: Hier noch meine funktionierende Testdatei:

          https://fsv-optik.de/test_bg2.html

          1. problematische Seite

            Hallo Nico R.,

            toggle() & Co im Self-Wiki

            Rolf

            --
            sumpsi - posui - obstruxi
            1. problematische Seite

              Hallo Rolf,

              danke, classList.add und .remove nutze ich lustigerweise regelmäßig, die kannte ich ebenfalls so aus jQuery. Keine Ahnung, warum mir .toggle entgangen ist. Erspart auf jeden Fall einiges an Schreibarbeit.

              Schöne Grüße Nico

          2. problematische Seite

            @@Nico R.

            Wunderbar, das funktioniert! 😀

            Aber schlecht.

            So einen Anfängerfehler hätte ich von @Rolf B nicht erwartet.

            Sehr schön, tausend Dank euch beiden und einen schönen Abend

            Man soll den Tag nicht vor dem Abend loben.

            Kwakoni Yiquan

            --
            Ad astra per aspera
        3. problematische Seite

          @@Rolf B

          so was?

          Nein! Das ist großer Mist.

          Ich hab's bewusst einfach gehalten.

          scroll-Eventhandler ohne Debouncing bzw. Throttling ist nicht „bewusst einfach“, sondern einfach nur Mist. Nicht machen, Kinder!

          Man braucht hier aber gar keinen scroll-Eventhandler, wenn man einen IntersectionObserver nehmen kann. Wir sprachen drübermehrfach.

          Kwakoni Yiquan

          --
          Ad astra per aspera
          1. problematische Seite

            Hallo Gunnar,

            Erst denken, bitte, dann bashen. Die Lösung ist kein Mist. Mist ist dein Schwarz-Weiß Denken.

            Erstens ist ein IntersectionObserver komplizierter. Zumindest für mich.

            Zweitens brauche ich keinen Debouncer, wenn ich nur eine Klasse umschalte. Beim Überschreiten der Schwelle löst das was aus, sonst passiert nichts. Der Debouncer hätte hier nur unnützen Overhead bedeutet. Ich hätte ihn erwähnen können, ja.

            Dass es sowas wie scroll-driven animation gibt, wusste ich noch, kam aber nicht mehr auf die richtigen Stichworte. Ich war aber sicher, dass du das bringen würdest 😉

            Rolf

            --
            sumpsi - posui - obstruxi
            1. problematische Seite

              @@Rolf B

              Erst denken, bitte, dann bashen.

              Danke, gleichfalls. 😜

              Zweitens brauche ich keinen Debouncer, wenn ich nur eine Klasse umschalte.

              Du tust aber nicht nur das. Wie du selbst sagst:

              Beim Überschreiten der Schwelle löst das was aus, sonst passiert nichts.

              Du fragst also ständig die aktuelle Scrollposition ab – zigfach pro Sekunde.

              Der Debouncer hätte hier nur unnützen Overhead bedeutet.

              Wirklich?


              Erstens ist ein IntersectionObserver komplizierter. Zumindest für mich.

              Das kann man ja lernen. Soll Entwicklerfaulheit jetzt ein Grund sein, Nutzern schlechte Performanz zuzumuten?

              Kwakoni Yiquan

              --
              Ad astra per aspera
              1. problematische Seite

                Hallo Gunnar,

                löst die Abfrage von pageYOffset ein Re-Layouting aus?

                Rolf

                --
                sumpsi - posui - obstruxi
                1. problematische Seite

                  @@Rolf B

                  löst die Abfrage von pageYOffset ein Re-Layouting aus?

                  Das war eine rhetorische Frage?

                  Ich weiß nicht, wie teuer die Abfrage im Vergleich zum Debouncer ist.

                  Kwakoni Yiquan

                  --
                  Ad astra per aspera
                  1. problematische Seite

                    Hallo Gunnar,

                    Ich weiß nicht, wie teuer die Abfrage im Vergleich zum Debouncer ist.

                    Was Dich nicht hindert, mir Unfähigkeit vorzuwerfen.

                    Da hilft dann nur Messen. Ich habe die Seite aus dem Fiddle geholt und in eine HTML Seite gesteckt hier und die Performance-Tools von Chrome mitlaufen lassen. Um deutlicher zu sehen, was passiert, habe ich den toggle aufgelöst und die eigentlichen Aktionen in separate Funktionen gesteckt, half aber nichts, Chrome hat mir keinen detaillierten Callstack gemacht. Hmpf. Die Aufrufe von classList.add und classList.remove sieht man aber - aber nur, wenn auch wirklich was passiert. Das erkennt man daran, dass nach diesen Aufrufen die Animation losläuft, die hat einen eigenen Balken im Performance-Graph.

                    Vermutlich passiert zu wenig, um ein Re-Layout auszulösen, und Chrome optimiert das weg. Dass pageYOffset eigentlich relayouten muss, ist schon naheliegend. window.scrollY ist schließlich in der Liste der Layout-forcierenden Funktionen drin.

                    Ich hab mal einen "margin-wackler" eingebaut, der bei jedem Scroll-Aufruf den Margin des Body verändert. Dann gibt's bei jedem scroll-Aufruf eine Layout-Phase während des Javascript. Wenn ich die pageYOffset-Abfrage, gibt's die Layout-Phase trotzdem, aber erst nach Script-Ende. Und wenn ich das hier mache, gibt's 2 Layoutphasen pro scroll-Event:

                    function handleScroll() {
                       document.body.style.setProperty("margin-inline", (8+d)+"px");
                       d = -d;
                    
                       changeBackground(); // enthält pageYOffset-Abfrage
                    
                       document.body.style.setProperty("margin-inline", (8+d)+"px");
                       d = -d;
                    }
                    

                    Kommentiere ich die changeBackground-Zeile aus, gibt's erwartungsgemäß keine Layoutphase im Eventhandler (außer beim ersten Mal).

                    Also, ja, pageYOffset hat das Potenzial zu einem Performancekiller. Ohne diese Messungen hier hätte man das aber nicht sicher beurteilen können. Für den Hintergrundbildwechsler ist das Throttling irrelevant.

                    Ich habe mal den Throttler aus dem Wiki eingebaut. Bei 50ms Throttlelimit hat man nicht viel davon, aber bei 100ms merkt man schon die leichte Verzögerung. 250ms sieht doof aus. Wie stark es sich auswirkt, hängt auch an der Framerate. Bei 60 Hz kommt das Scroll-Event alle 16,7ms. Ich habe 100Hz, da sind es 10ms und 50ms Throttle-Limit fünfteln die Anzahl der Abfragen schon. Mein "Mess-Ablauf": Performancetools einschalten, Seite runter, rauf, runter, rauf scrollen, Performancetools stoppen. Jeweils ca 3s Dauer. Ohne Throttle sinds ca 100ms Renderingzeit, mit Throttle geht es auf 25-40 runter, je nach Stärke. Das liegt aber am Margin-Wackler. Nehme ich den raus, hat das Throttling keinen messbaren Einfluss mehr auf die Renderingzeit.

                    Kannst gern selbst messen.

                    Rolf

                    --
                    sumpsi - posui - obstruxi
          2. problematische Seite

            Hallo Gunnar,

            es ist zum Kotzen, in einem iframe funktionieren bei root==null die rootMargin-Angaben für den IntersectionObserver nicht. Zumindest nicht in einem jsFiddle. Kein Wunder, dass ich nie mit dem Ding warm wurde 😉

            Rolf

            --
            sumpsi - posui - obstruxi
    3. problematische Seite

      @@Rolf B

      du kannst Transitionen auf Eigenschaften anwenden, die einen kontinuierlichen Wertebereich haben. Das sind zum einen Zahlen, und für Farben kriegt der Browser das auch hin.

      Schlüsselworteigenschaften oder Bilder sind nicht kontinuierlich.

      Eine Überblendung zwischen zwei Bildern geht nur per Transparenz

      Dafür, dass es nicht geht, geht’s schon ganz gut. 😜 (Safari, Chromia; Firefox ab 131)

      Kevin Powell: We can now transition to and from display: none

      Kwakoni Yiquan

      --
      Ad astra per aspera
  3. problematische Seite

    @@Nico R.

    ich versuche ein background-image per JS auszutauschen

    Hier steckt schon der erste Fehler. Warum per JavaScript?

    Möglicherweise braucht man kein JavaScript. Soll der Bildwechsel bei einer bestimmten absoluten Scrollposition vollzogen werden oder bei einer relativen (bezogen auf den gesamten Scrollweg)? Dann geht’s mit scroll-driven animation, guckst du.

    Kwakoni Yiquan

    --
    Ad astra per aspera
    1. problematische Seite

      Hallo Gunnar,

      Hier steckt schon der erste Fehler. Warum per JavaScript?

      Weil ich ja die Scrollposition überwachen will. Ich wusste nicht, dass das auch per CSS geht. Ich hätte natürlich statt des background-image eine Klasse autauschen können. Aber eine CSS-Manipulation bleibts ja.

      Möglicherweise braucht man kein JavaScript. Soll der Bildwechsel bei einer bestimmten absoluten Scrollposition vollzogen werden oder bei einer relativen (bezogen auf den gesamten Scrollweg)?

      Klingt interessant. Bei einer absoluten Position. Immer, wenn window.pageYOffset > 100

      Dann geht’s mit scroll-driven animation, guckst du.

      Bei mir funktioniert die Demo nicht. https://scroll-driven-animations.style/ sagt: Your browser does not support Scroll-Linked Animations. Mein Firefox ist aktuell (128.0).

      Damit ist die Variante für die nähere Zeit eigentlich schon raus und es bleibt wohl bei der JS-Lösung.

      Ich werde mir noch nochmal die Sache mit diesem IntersectionObserver anschauen. Ich habe aber nicht den Eindruck, dass mein Browser mit dem Abhorchen des scroll-EventListeners Probleme hat. Wenn ich mir anschaue, was auf manchen Seiten so im Hintergrund abgeht, ohne dass der Browser murrt, sehe ich das auch recht entspannt, denn meine Seiten versuche ich schon, so gut wie möglich schlank zu halten.

      Gruß Nico

      1. problematische Seite

        Guten Abend,

        [...] denn meine Seiten versuche ich schon, so gut wie möglich schlank zu halten.

        Sehr gut! Auch wenn andere Webseiten über 120MB ausliefern, ist das doch das Ideal, das wir anstreben.

        Weil ich ja die Scrollposition überwachen will. Ich wusste nicht, dass das auch per CSS geht.

        Da wäre der erste Grundsatz, den man erwähnen, bzw. beachten sollte:

        Nimm für die Darstellung CSS. Da geht fast alles mit Media Queries oder eben in Ausnahmefällen mit JavaScript, z.B. windows.matchMedià oder dem Intersection Observer.

        Ich werde mir noch nochmal die Sache mit diesem IntersectionObserver anschauen.

        Hier ist ein Beispiel im Wiki:

        Das Ganze müsste man noch ausbauen, bzw. von einem anderen Blickwinkel (anderes Beispiel) aus betrachten.

        Ich habe aber nicht den Eindruck, dass mein Browser mit dem Abhorchen des scroll-EventListeners Probleme hat.

        Achtung Der Wurm muss dem Fisch und nicht dem Angler schmecken! [1] Wenn du auf dem Zeltplatz mit Handy und schlechtem Netz unterwegs bist, …

        Und da sind wir wieder bei der ersten Aussage:

        [...] denn meine Seiten versuche ich schon, so gut wie möglich schlank zu halten.

        Lass Deine Schleife nicht über jede Pixeländerung 1000x drüber laufen, sondern frage ab, wann der bestimmte Punkt erreicht ist.


        Ich werde die Wiki-Seiten zu den scroll-Eigenschaften die Tage mit einem Link zum Intersection Observer ergänzen.

        Herzliche Grüße

        Matthias Scharwies

        --
        Die Signatur findet sich auf der Rückseite des Beitrags.

        1. Geklaut zitiert von @Gunnar Bittersmann ↩︎

        1. problematische Seite

          Hallo Matthias,

          Lass Deine Schleife nicht über jede Pixeländerung 1000x drüber laufen, sondern frage ab, wann der bestimmte Punkt erreicht ist.

          Dieser Grundsatz gefällt mir sehr gut. Wobei mir das Prinzip noch nicht ganz klar ist. Dem werd ich mich nochmal widmen und vermutlich in Kürze nachfragen... Gerade brüte ich schon wieder überm nächsten Problem.

          Gruß Nico

          1. problematische Seite

            @@Nico R.

            Lass Deine Schleife nicht über jede Pixeländerung 1000x drüber laufen, sondern frage ab, wann der bestimmte Punkt erreicht ist.

            Dieser Grundsatz gefällt mir sehr gut. Wobei mir das Prinzip noch nicht ganz klar ist.

            Der IntersectionObserver erkennt, wenn der bestimmte Punkt erreicht ist, und führt die dann vorgehene Aktion aus.

            Ich vermute, dass Browser das performanter implementiert haben als man selbst irgendwas mit einem scroll-Eventhandler hinbekäme.

            Kwakoni Yiquan

            --
            Ad astra per aspera
        2. problematische Seite

          @@Matthias Scharwies

          Ich habe aber nicht den Eindruck, dass mein Browser mit dem Abhorchen des scroll-EventListeners Probleme hat.

          Achtung Der Wurm muss dem Fisch und nicht dem Angler schmecken! ¹ Wenn du auf dem Zeltplatz mit Handy und schlechtem Netz unterwegs bist, …

          Du spielst hier auf die Ladezeit einer Seite an. Hier geht’s aber um die Performanz einer Seite, wenn sie schon geladen ist. Also: Wenn du auf dem Zeltplatz mit schlechtem Handy unterwegs bist. (Oder anderswo.)

          Wobei „schlecht“ nicht das passende Wort ist; das kann ein älteres Modell sein oder ein preiswertes. Schlecht sind die Webseiten, die für die nagelneuen besten Handys optimiert sind, also gegen alle anderen. (nach Cheatah)

          Und schlechte Performanz macht sich nicht nur durch Ruckeln bei Interaktionen bemerkbar, sondern auch durch hohen Stromverbrauch, was den Akku schneller leersaugt.

          Kwakoni Yiquan

          --
          Ad astra per aspera
      2. problematische Seite

        @@Nico R.

        Möglicherweise braucht man kein JavaScript. Soll der Bildwechsel bei einer bestimmten absoluten Scrollposition vollzogen werden oder bei einer relativen (bezogen auf den gesamten Scrollweg)?

        Klingt interessant. Bei einer absoluten Position. Immer, wenn window.pageYOffset > 100

        Hm, vielleicht geht auch das nur mit CSS ohne JavaScript? Muss ich mal in Ruhe drüber nachdenken.


        Dann geht’s mit scroll-driven animation, guckst du.

        Bei mir funktioniert die Demo nicht. https://scroll-driven-animations.style/ sagt: Your browser does not support Scroll-Linked Animations. Mein Firefox ist aktuell (128.0).

        Huch? Sollte ich … Ah ja, ich hab in meinen Fuchs das Feature-Flag layout.css.scroll-driven-animations.enabled gesetzt. Vermutlich, als ich da mal mit rumgespielt hatte.

        Laut Can I Use bleibt das Feature auch noch für die nächsten Wochen hinterm Flag versteckt.


        Damit ist die Variante für die nähere Zeit eigentlich schon raus und es bleibt wohl bei der JS-Lösung.

        Warum? Ein Hintergrundbild ist per se schon Verzierung. Das Wechseln des Hintergrundbilds ist Schnickschnack – nice to have, aber wenn’s in manchen Browsern noch nicht geht, ist das auch nicht schlimm. Da kann man auf progressive enhancement setzen.

        Aber wie gesagt, für das Wechseln nach 100px Runterscrollen ist mir auch keine reine CSS-Lösung eingefallen, deshalb hatte ich das ja auch mit JavaScript gemacht …

        Ich werde mir noch nochmal die Sache mit diesem IntersectionObserver anschauen.

        … und schalte mit IntersectionObserver eine Klasse um, worüber die Sichtbarkeit der Trennlinie unterm Header und des Footers geregelt wird. Bei dir wär’s dann das Hintergrundbild.


        Ich habe aber nicht den Eindruck, dass mein Browser mit dem Abhorchen des scroll-EventListeners Probleme hat.

        Wie @Matthias Scharwies schon sagte: dein Browser ist irrelevant. Relevant sind die Browser deiner Seitenbesucher.

        Kwakoni Yiquan

        --
        Ad astra per aspera