Der folgende Beitrag ist ein Gastbeitrag von Emil Björklund. Filtereffekte in CSS gibt es schon eine Weile, und zusammen mit Dingen wie Mischmodi eröffnen sie neue Möglichkeiten, Elemente im Browser nachzubilden und zu manipulieren, die wir früher in Photoshop machen mussten. Hier untersucht Emil eine Leistungstechnik, die einen der vergesseneren Filtereffekte – die filter()-Funktion – nutzt und sie auch mit SVG nachbildet.
Das alles beginnt mit einem Artikel des Facebook-Engineering-Teams, wie sie Vorschaubilder für Titelbilder laden in ihren nativen Apps. Das Problem, mit dem sie konfrontiert waren, ist, dass diese „Titelbilder“ groß sind und oft eine Weile zum Laden brauchen, was dem Benutzer ein weniger als ideales Erlebnis hinterlässt, wenn der Hintergrund plötzlich von einer Volltonfarbe zu einem Bild wechselt.
Dies gilt insbesondere bei geringer Konnektivität oder mobilen Netzwerken, bei denen man oft auf eine leere graue Box starrt, während man auf den Download von Bildern wartet.
Idealerweise wäre das Bild in die ursprüngliche API-Antwort ihrer App beim Abrufen der Profildaten kodiert. Aber um in diese Anfrage zu passen, müsste das Bild auf 200 Bytes begrenzt sein. Problematisch, da Titelbilder über 100 Kilobytes groß sind.
Wie holt man also etwas Wertvolles aus 200 Bytes heraus und wie zeigt man dem Benutzer etwas an, bevor das Bild vollständig geladen ist?
Die (geniale) Lösung war, ein winziges Bild (ca. 40 Pixel breit) zurückzugeben und dieses winzige Bild dann hochzuskalieren und einen Gaußschen Weichzeichner anzuwenden. Dies zeigt sofort einen Hintergrund, der ästhetisch ansprechend aussieht und eine Vorschau darauf gibt, wie das Titelbild aussehen würde. Das eigentliche Titelbild könnte dann rechtzeitig im Hintergrund geladen und nahtlos eingeblendet werden. Klug gedacht!
Diese Technik hat ein paar coole Aspekte:
- Sie macht die wahrgenommene Ladezeit unglaublich schnell.
- Sie nutzt etwas, das traditionell teuer in Bezug auf die Leistung ist, um die Leistung zu verbessern.
- Sie ist im Web machbar.
Große Header-Hintergrundbilder (und ihre Leistungsprobleme) sind definitiv etwas, womit wir uns beim Erstellen für das Web auseinandersetzen können, also sind das nützliche Dinge. Wir versuchen vielleicht, den Download von großen Bildern zu vermeiden, aber manchmal gehen wir den Kompromiss ein, um eine bestimmte Stimmung zu erzielen. Das Beste, was wir in dieser Situation tun können, ist, die wahrgenommene Leistung zu optimieren, also können wir diese Technik genauso gut stehlen.
Ein funktionierendes Beispiel
Wir werden diese Header-Bildfunktion mit einem Ansatz vom Typ „kritische CSS“ nachbilden. Die allererste Anfrage lädt das winzige Bild in Inline-CSS, dann kommt das hochauflösende Hintergrundbild nach dem ersten Rendern.
Es wird ungefähr so aussehen, wenn es fertig geladen ist

In diesem Beispiel verwenden wir ein Hintergrundbild und betrachten es als Dekoration und nicht als Teil des Inhalts. Es gibt einige feinere Punkte zu diskutieren, wann diese Arten von Bildern als Inhalt angesehen werden (und somit als <img> kodiert werden) und wann sie Hintergrundbilder sind. Um clevere Größenmodi (wie die CSS-Werte cover und contain) nutzen zu können, sind Hintergrundbilder wahrscheinlich die gängigste Lösung für diese Art von Designs, aber neue Eigenschaften wie object-fit machen denselben Ansatz für Inhaltsbilder etwas einfacher. Seiten wie Medium verwenden bereits weichgezeichnete Inhaltsbilder, um Ladezeiten zu verbessern, aber die Nützlichkeit dieser Technik ist diskutierbar – bringen die weichgezeichneten Bilder etwas, wenn die Ladetechnik fehlschlägt? Wie auch immer: In diesem Artikel konzentrieren wir uns auf diese Technik, wie sie auf Hintergrundbilder angewendet wird.
Hier ist die Übersicht, wie es funktionieren wird:
- Inline ein winziges Vorschaubild (40×22 Pixel) als base64-kodiertes Hintergrundbild innerhalb eines
<style>-Tags. Der Style-Tag enthält auch allgemeine Styling- und die Regeln für die Anwendung eines Gaußschen Weichzeichners auf das Hintergrundbild. Schließlich enthält er Stile für die größere Version des Header-Bildes, die auf einen anderen Klassennamen beschränkt sind. - Holen Sie sich die URL zum großen Bild aus dem Inline-CSS und laden Sie es vorab mit JavaScript. Wenn das Skript aus irgendeinem Grund fehlschlägt, kein Schaden, kein Foul – das weichgezeichnete Hintergrundbild ist immer noch da und sieht ziemlich cool aus.
- Wenn das große Bild geladen ist, fügen Sie einen Klassennamen hinzu, der das CSS umschaltet, um das große Bild als Hintergrund zu verwenden und den Weichzeichner zu entfernen. Hoffentlich kann das Entfernen des Weichzeichners auch animiert werden.
Sie finden das endgültige Beispiel als Pen. Sie werden wahrscheinlich das weichgezeichnete Bild für einen Moment sehen, bevor das schärfere Bild geladen wird. Wenn nicht, versuchen Sie, die Seite mit einem leeren Cache neu zu laden.
Ein winziges, optimiertes Bild
Zunächst benötigen wir eine Vorschaubild-Version des Bildes. Facebook hat die Größe ihrer Bilder durch Kompressionszauberei auf 200 Bytes reduziert (z. B. Speichern der unveränderlichen JPEG-Header-Teile in der App), aber wir werden nicht ganz so extrem vorgehen. Mit einer Größe von 40 x 22 Pixeln kommt dieses spezielle Bild nach der Verarbeitung durch einige Bildoptimierungssoftware auf etwa 1000 Bytes.

Das vollständige JPEG-Bild ist etwa 120 KB bei 1500 × 823 Pixeln. Diese Dateigröße könnte wahrscheinlich deutlich niedriger sein, aber wir belassen sie so, da dies ein Proof of Concept ist. In einem realen Beispiel hätten Sie wahrscheinlich einige Größenvarianten des Bildes und würden je nach Ansichtsfenstergröße eine andere laden – vielleicht sogar ein anderes Format wie WebP.
Die filter-Funktion für Bilder
Als Nächstes möchten wir das winzige Bild so skalieren, dass es das Element abdeckt, aber wir wollen nicht, dass es pixelig und hässlich aussieht. Hier kommt die filter()-Funktion ins Spiel. Filter in CSS können etwas verwirrend erscheinen, da es effektiv drei Arten gibt: die filter-Eigenschaft, ihr vorgeschlagenes backdrop-filter-Gegenstück (in der Filter Effects Level 2-Spezifikation) und schließlich die filter()-Funktion für Bilder. Werfen wir zunächst einen Blick auf die Eigenschaft.
.myThing {
filter: hue-rotate(45deg);
}
Ein oder mehrere Filter werden angewendet, wobei jeder auf dem Ergebnis des vorherigen basiert – sehr ähnlich einer Liste von Transformationen. Es gibt eine ganze Reihe vordefinierter Filter, die wir verwenden können: blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), sepia() und saturate().
Noch cooler ist, dass dies eine Spezifikation ist, die zwischen CSS und SVG geteilt wird. Nicht nur sind die vordefinierten Filter in Bezug auf SVG spezifiziert, wir können auch unsere eigenen Filter in SVG erstellen und sie aus CSS referenzieren.
.myThing {
filter: url(myfilter.svg#myCustomFilter);
}
Dieselben Filtereffekte sind in backdrop-filter gültig und werden beim Compositing eines transparenten Elements mit seinem Hintergrund angewendet – vielleicht am nützlichsten für die Erstellung des „Milchglas“-Effekts.
Schließlich gibt es die filter()-Funktion für Bildwerte. Die Idee ist, dass überall dort, wo Sie ein Bild in CSS referenzieren können, Sie es auch durch eine Liste von Filtern leiten können. Für das winzige Header-Bild betten wir es als Base64 DataURI ein und führen es durch den blur()-Filter.
.post-header {
background-image: filter(url(data:image/jpeg;base64,/9j/4AAQ ...[truncated] ...), blur(20px));
}
Das ist großartig, da dies genau das ist, wonach wir suchen, wenn wir die Technik aus der Facebook-App nachbilden! Allerdings gibt es schlechte Nachrichten in Bezug auf die Unterstützung. Die filter-Eigenschaft wird in den neuesten Versionen aller Browser außer IE unterstützt, aber keiner davon außer WebKit hat den filter()-Funktionsteil der Spezifikation implementiert.
Wenn ich hier WebKit sage, meine ich die WebKit-Nightly-Builds zum Zeitpunkt der Erstellung dieses Beitrags und nicht Safari. Die filter-Funktion für Bilder ist technisch in iOS9 als -webkit-filter() enthalten, aber das wurde soweit ich finden kann nirgends offiziell gemeldet, was etwas seltsam ist. Der Grund dafür ist wahrscheinlich ein furchtbarer Bug mit background-size: Das Originalbild wird nicht neu skaliert, aber die gekachelte Größe des gefilterten Ausgabebildes. Das bricht die Funktionalität von Hintergrundbildern ziemlich stark, besonders mit Weichzeichnung. Es wurde behoben, aber nicht rechtzeitig für die Safari 9-Version, also gehe ich davon aus, dass sie diese Funktion nicht ankündigen wollten.
Aber was machen wir mit der fehlenden/kaputten filter()-Funktionalität? Wir könnten entweder Browsern, die sie nicht unterstützen, einen Volltonhintergrund geben, bis das Bild geladen ist, obwohl das bedeutet, dass sie gar keinen Hintergrund erhalten, wenn JS fehlschlägt. Langweilig!
Nein, wir behalten die filter()-Funktion als zusätzliches Gewürz für die Animation des später eingeblendeten Bildes auf und emulieren stattdessen die Filterfunktion für das anfängliche Bild mit SVG.
Nachbildung des Weichzeichnerfilters mit SVG
Da die Spezifikation praktisch eine SVG-Entsprechung für den blur()-Filter bietet, können wir nachbilden, wie der Weichzeichnerfilter in SVG funktioniert, mit ein paar Anpassungen.
- Die Ränder werden beim Anwenden des Gaußschen Weichzeichners etwas halbtransparent. Wir können dies beheben, indem wir einen sogenannten
feComponentTransfer-Filter hinzufügen. Die Komponentenübertragung ermöglicht es Ihnen, jeden Farbkanal (einschließlich Alpha) einer Quellgrafik zu manipulieren. Diese spezielle Variante verwendet dasfeFuncA-Element, das jeden Wert zwischen0und1im Alpha-Kanal auf1abbildet, was bedeutet, dass es jede Alpha-Transparenz entfernt. - Das Attribut
color-interpolation-filtersauf dem<filter>-Element muss aufsRGBgesetzt werden. SVG-Filter verwenden standardmäßig denlinearRGB-Farbraum, und CSS arbeitet insRGB. Die meisten Browser scheinen die Farbkorrekturen richtig zu handhaben, aber Safari/WebKit macht die Farben ausgewaschen, es sei denn, dieser Wert ist gesetzt. - Die
filterUnitsist aufuserSpaceOnUsegesetzt, was vereinfacht ausgedrückt bedeutet, dass Koordinaten und Längen (wie diestdDeviationdes Weichzeichners) Pixel auf dem Element abbilden, auf das wir den Weichzeichner anwenden.
Der resultierende SVG-Code sieht ungefähr so aus:
<filter id="blur" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="20" edgeMode="duplicate" />
<feComponentTransfer>
<feFuncA type="discrete" tableValues="1 1" />
</feComponentTransfer>
</filter>
Die filter-Eigenschaft verwendet ihre eigene url()-Funktion, in der wir entweder einen SVG-Filter referenzieren oder URI-kodieren können. Wie wenden wir also einen Filter auf etwas innerhalb eines background-image: url(...) an?
Nun, SVG-Dateien können auf andere Bilder verweisen, und wir können Filter auf diese Bilder innerhalb des SVG anwenden. Das Problem ist, dass SVG-Hintergrundbilder keine externen Ressourcen abrufen können. Aber wir können dies umgehen, indem wir das JPG innerhalb des SVG base64-kodieren. Dies wäre bei einem großen Bild nicht praktikabel, aber für unser winziges sollte es in Ordnung sein. Das SVG wird so aussehen:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1500" height="823"
viewBox="0 0 1500 823">
<filter id="blur" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="20 20" edgeMode="duplicate" />
<feComponentTransfer>
<feFuncA type="discrete" tableValues="1 1" />
</feComponentTransfer>
</filter>
<image filter="url(#blur)"
xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJ ...[truncated]..."
x="0" y="0"
height="100%" width="100%"/>
</svg>
Ein weiterer Nachteil (im Vergleich zur reinen Verwendung der filter()-Funktion mit einer Bitmap) ist, dass wir einige Größen manuell festlegen müssen, damit das SVG richtig mit der Hintergrundgröße zusammenarbeitet. Das SVG selbst hat eine viewBox, die eingestellt ist, um das Seitenverhältnis des Bildes nachzuahmen, und die Eigenschaften width und height sind auf dieselben Maße gesetzt, um sicherzustellen, dass es browserübergreifend funktioniert (z. B. IE verdirbt das Seitenverhältnis, wenn diese fehlen). Schließlich ist das <image>-Element so eingestellt, dass es die gesamte SVG-Leinwand abdeckt.
Jetzt können wir diese Datei als Hintergrund für den Post-Header verwenden, und es wird ungefähr so aussehen:

Als letzten Schritt können wir das SVG-Wrapper-Bild inline in das CSS einfügen, um eine zusätzliche Anfrage zu vermeiden. Inline-SVG muss URI-kodiert sein, ich benutze yoksel's SVG-Encoder dafür. Jetzt haben wir eine dataURI, die eine andere dataURI enthält. DataURInception!
Beim Kodieren des SVG erhalten wir Text zum Einfügen in die url(), aber es ist erwähnenswert, dass wir einige Metadaten voranstellen müssen, damit es angezeigt wird: data:image/svg+xml;charset=utf-8,. Das charset-Zeug ist wichtig: Es sorgt dafür, dass das kodierte SVG über Browser hinweg gut funktioniert.
.post-header {
background-color: #567DA7;
background-size: cover;
background-image: url(data:image/svg+xml;charset=utf-8,%3Csvg...);
}
Zu diesem Zeitpunkt beträgt die gesamte Seite, einschließlich des Bildes, 1 Anfrage und 5KB bei Verwendung von GZIP.
Abrufen der URL für das große Bild
Als Nächstes erstellen wir eine Regel für den erweiterten Header, in der wir das riesige Hintergrundbild einrichten.
.post-header-enhanced {
background-image: url(largeimg.jpg);
}
Anstatt nur den Klassennamen umzuschalten, wodurch das große Bild geladen wird, möchten wir das große Bild vorab laden und dann den Klassennamen anwenden. Dies geschieht, damit wir den Wechsel später reibungslos animieren können, in der vernünftigen Annahme, dass das große Bild fertig geladen ist. Da wir die Bild-URL nicht sowohl im CSS als auch in JavaScript hart kodieren wollen, holen wir uns die URL mit JavaScript aus den Stilen. Da der Klassename noch nicht angewendet ist, können wir nicht einfach nach headerElement.style.backgroundImage usw. suchen – es kennt den Hintergrund noch nicht. Um dies zu lösen, verwenden wir das CSSOM – das CSS-Objektmodell und die schreibgeschützten JS-Eigenschaften, die es uns ermöglichen, die CSS-Regeln zu durchlaufen.
Der folgende Schnipsel findet den Klassennamen für den erweiterten Header, extrahiert dann die URL mit etwas Regex. Danach lädt er das Bild vorab und löst den hinzugefügten Klassennamen aus, sobald dies geschehen ist.
<script>
window.onload = function loadStuff() {
var win, doc, img, header, enhancedClass;
// Quit early if older browser (e.g. IE 8).
if (!('addEventListener' in window)) {
return;
}
win = window;
doc = win.document;
img = new Image();
header = doc.querySelector('.post-header');
enhancedClass = 'post-header-enhanced';
// Rather convoluted, but parses out the first mention of a background
// image url for the enhanced header, even if the style is not applied.
var bigSrc = (function () {
// Find all of the CssRule objects inside the inline stylesheet
var styles = doc.querySelector('style').sheet.cssRules;
// Fetch the background-image declaration...
var bgDecl = (function () {
// ...via a self-executing function, where a loop is run
var bgStyle, i, l = styles.length;
for (i=0; i<l; i++) {
// ...checking if the rule is the one targeting the
// enhanced header.
if (styles[i].selectorText &&
styles[i].selectorText == '.'+enhancedClass) {
// If so, set bgDecl to the entire background-image
// value of that rule
bgStyle = styles[i].style.backgroundImage;
// ...and break the loop.
break;
}
}
// ...and return that text.
return bgStyle;
}());
// Finally, return a match for the URL inside the background-image
// by using a fancy regex I Googled up, as long as the bgDecl
// variable is assigned at all.
return bgDecl && bgDecl.match(/(?:\(['|"]?)(.*?)(?:['|"]?\))/)[1];
}());
// Assign an onLoad handler to the dummy image *before* assigning the src
img.onload = function () {
header.className += ' ' +enhancedClass;
};
// Finally, trigger the whole preloading chain by giving the dummy
// image its source.
if (bigSrc) {
img.src = bigSrc;
}
};
</script>
Das Skript wird frühzeitig beendet, wenn addEventListener nicht unterstützt wird, was schön mit dem Rest der benötigten Unterstützung übereinstimmen sollte. Soweit ich das beurteilen kann, unterstützen alle einigermaßen modernen SVG-fähigen Browser den Rest des CSSOM und andere verwendete JavaScript-Funktionen.
Animation des Wechsels
Es ist ein bisschen enttäuschend, dass wir die filter()-Funktion nicht verwenden konnten, nachdem wir herausgefunden hatten, dass sie existiert und alles. Also fügen wir einen animierten Effekt hinzu, wenn wir das hochauflösende Bild einwechseln. Dies funktioniert derzeit nur in WebKit-Nightlies, und wir können die @supports-Regel sicher verwenden, um die Änderungen zu begrenzen. Hier ist ein animiertes GIF, das den Effekt in Aktion zeigt:

Beachten Sie, dass wir hierfür keine transition verwenden können: Die filter()-Funktion ist animierbar, aber nur für Werteänderungen in der Filterkette – wenn sich das Hintergrundbild ändert, haben wir Pech gehabt. Wir können jedoch eine Animation dafür verwenden, aber das bedeutet, dass wir die URL des Hintergrundbilds noch zweimal wiederholen müssen, als Start- und Endwerte. Ein kleiner Preis dafür.
Hier sind die CSS-Stile für die erweiterten Header-Stile für Browser, die die filter()-Funktion verstehen:
@supports (background-image: filter(url('i.jpg'), blur(1px))) {
.post-header {
transform: translateZ(0);
}
.post-header-enhanced {
animation: sharpen .5s both;
}
@keyframes sharpen {
from {
background-image: filter(largeimg.jpg), blur(20px));
}
to {
background-image: filter(largeimg.jpg), blur(0px));
}
}
}
Ein letztes Detail ist der translateZ(0)-Trick auf dem Header hier: Ohne ihn ist die Animation verrückt ruckelig. Ich habe versucht, ganz modern zu sein und will-change: background-image zu verwenden, aber das überzeugte den Browser nicht, eine Hardware-beschleunigte Ebene zu erstellen, also musste ich auf den alten Trick zurückgreifen, einen 3D-„Null-Transformations-Trick“ hinzuzufügen.
Schnelle, progressiv verbesserte Hintergrundbilder
Da haben wir es, eine Seite mit einem riesigen Hintergrundbild (wenn auch unscharf) lädt in 5 KB, lazy-loading das scharf aussehende Vollbildbild. Im Moment kann nur WebKit das schärfere Bild animieren, aber ich hoffe, dass andere Browser die filter()-Funktion bald implementieren werden. Ich bin sicher, dass wir sie noch für viele weitere unterhaltsame Techniken nutzen können.
Ein Wort für dich: episch. Danke!
Sehr cool.
Was denkst du über die gesamte Umsetzung in CSS?
So etwas wie das…
Danke!
Ich bin mir nicht ganz sicher, was du mit „alles in CSS“ meinst und was dein Code anders machen soll – vielleicht könntest du das etwas klarstellen? Ich bin auf jeden Fall dafür, mehr Dinge mit CSS zu machen. :-)
Hallo Emil,
Entschuldigung, ich merke, dass das nicht funktionieren würde, es war ein langer Tag, aber ich denke immer noch, dass es möglich ist.
Wenn ich es richtig verstehe, verwendest du JS, um das große Bild vorab zu laden, fügst dann eine Klasse zum Element hinzu, um das Bild einzublenden.
http://codepen.io/M_J_Robbins/pen/aOxPXe/
Hier lade ich das kleine Bild inline, wie du es tust.
Dann lade ich nach 1 Sekunde das große Bild mit Animation vor.
Dann nach 5 Sekunden blende ich es ein. (Zeiten sind zur Verdeutlichung übertrieben, ich bin mir nicht sicher, was das Optimum wäre).
Viele Grüße,
Mark
Ah, verstehe. Ja, das ist ziemlich cool, ich denke, das einzige Problem wäre, dass es ein „Schätzerät“ wäre, wann das große Bild fertig geladen ist. Ich glaube, meine Idee war, „etwas wahnsinnig schnell zu laden und dann mit JS zu entscheiden, was als Nächstes zu tun ist“, was zu anderen Lösungen verallgemeinert werden könnte – Bildformate, das große Bild gar nicht zu laden…
Ah ja, das ergibt Sinn, mir fällt keine Möglichkeit ein, das allein in CSS zu erkennen.
Ich bin ein E-Mail-Entwickler, also suche ich immer nach JS-freien Lösungen :)
Beste Grüße
Guter Beitrag, Emil! Er brachte mich dazu, dass es cool wäre, diese Technik zum Laden von progressiven JPEGs zu verwenden, wobei der Weichzeichner beim
loadend-Ereignis umgeschaltet wird. Hast du darüber nachgedacht? Vielleicht überwiegt der Overhead der Anfrage + dem Laden des allerersten Teils des Bildes die minimale zusätzliche Arbeit, ein Miniaturbild hinzuzufügen?Danke, Simon! (und hallo auch! lange nicht gesehen!)
Das ist eine sehr coole Idee, aber ich vermute, dass es nur für
<img>nützlich wäre, oder? Keine Ladeereignisse für CSS-Hintergrundbilder… Ich bin etwas skeptisch, Weichzeichnung für Inhaltsbilder zu verwenden, da das weichgezeichnete Bild nichts hinzufügt, es sei denn, es wird richtig geladen, während ein Hintergrundbild dekorativ bleiben kann, wenn es weichgezeichnet ist.Wie verhält sich das im Vergleich zu einem progressiven JPEG?
Artikel wie dieser erinnern mich daran, wie falsch es war, lowsrc obsolet zu machen.
Toller Beitrag und clever! Der Effekt wirft für mich Bedenken hinsichtlich der Barrierefreiheit auf.
Ich befürchte, dass Weichzeichnungs-/Fokusmuster (im Allgemeinen) für Benutzer mit Seh- oder kognitiven Einschränkungen etwas störend sein könnten. Wenn ein Benutzer beispielsweise bereits Schwierigkeiten hat, sich auf den Inhalt zu konzentrieren, könnte dies noch schwieriger sein, wenn der Hintergrund ebenfalls in den Fokus rückt… ein fast Tiefenschärfe-Effekt.
Das Web ist für viele Benutzer da draußen bereits verschwommen, und das ruckartige Laden eines Bildes, auch wenn es progressiv ist, hat möglicherweise weniger Auswirkungen als ein allmählicher Weichzeichner-zu-Fokus-Übergang wie dieser.
Da der Effekt JavaScript benötigt, um dem Benutzer das klare Bild zuzuführen, und da Sie nicht garantieren können, wann der Browser des Benutzers dieses JavaScript herunterlädt und ausführt, kann der Effekt jederzeit auftreten… oder vielleicht auch gar nicht. Dies könnte das Auge des Benutzers vom Inhalt ablenken, um mit dem Effekt zu ringen und dann zurück zum Inhalt.
Es spielt eine immer geringere Rolle, ob die Seite schnell geladen wird, wenn das Auge des Benutzers mit dem Fokus kämpft.
Hallo Joe, danke für das Feedback. Du sprichst einen wirklich guten Punkt an, und vielleicht einen, der für viele verschiedene Szenarien mit Animationen gilt – er birgt die Gefahr, abzulenken und zu verwirren. Wenn es signifikante Beweise dafür gibt, dass ein animierter Fokus-Effekt schlecht für die Barrierefreiheit ist, würde ich diesen Teil in einem „Live“-Produkt gerne weglassen.
Die Animation ist jedoch nicht entscheidend für die Technik – sie war eher eine unterhaltsame Möglichkeit, zu demonstrieren, was die
filter()-Funktion leisten kann. Der Teil „winziges Bild + Weichzeichnung = superschnelles erstes Rendern“ ist meiner Meinung nach viel interessanter, plus die „Polyfilling“ derfilter()-Funktion in SVG.Wenn ich nichts übersehe – wenn Sie es einmal scharf gesehen haben, werden Sie es nie wieder unscharf sehen? So läuft das CodePen-Beispiel jedenfalls.
Aber vielleicht übersehe ich etwas :(
Das ist ideal ;)
Das Bild ist zwischengespeichert und benötigt keine Weichzeichnung.
Aber wie Emil sagt:
Es liegt daran, dass Sie das Bild bereits im Browser-Cache haben.
Versuchen Sie, es in einem anderen Browser oder einem Inkognito-Fenster zu öffnen.
Ich denke, das ist ein nützlicher Proof of Concept, aber ich möchte eine Anmerkung hinzufügen:
Wenn Sie an einer großen, komplizierten Website arbeiten, werden Sie oft feststellen, dass Ressourcen das Rendern blockieren, dazu gehören alle CSS und alle synchronen JS im Kopfbereich. Das Problem dabei ist, dass selbst Ihr Inline-Base64-winziges Bild unter ungünstigen Bedingungen nicht in den ersten paar Sekunden zu zeichnen beginnt, sodass Benutzer effektiv immer noch weißem Leerraum für einige Sekunden gegenüberstehen, dann dem Weichzeichnerbild, fast unmittelbar gefolgt vom eigentlichen Bild.
Dies ist kein Fehler in Ihrem Ansatz, ich möchte nur betonen, dass das Inline-CSS nicht bedeutet, dass es sofort gerendert wird. Diese Deklaration muss immer noch warten, bis ALL CSS auf der Seite geladen und geparst ist. Sie könnten sogar eine Volltonhintergrundfarbe einfügen, und sie wird erst gerendert, nachdem alles CSS geladen und geparst ist.
Daher ist diese Lösung nur auf Websites sinnvoll, die das Rendern nicht blockieren, und meiner Erfahrung nach sind nur wenige Websites so optimiert.
Während wir dabei sind, eine zweite Anmerkung: Wenn Sie stattdessen ein Vordergrundbild verwenden würden, könnte dieses tatsächlich schneller laden als Ihr aktuelles Weichzeichnerbild. Der Grund dafür ist der Look-Ahead-Parser des Browsers, der sofort mit dem Laden des besagten Bildes beginnt, während Ihr Weichzeichnerbild immer noch auf das gesamte CSS der Seite sowie auf alle blockierenden JS wartet.
Zusammenfassend lässt sich sagen, dass die tatsächliche Realität möglicherweise nicht dem entspricht, was viele erwarten würden:
Inline bedeutet nicht sofortiges Rendern, es kann immer noch Sekunden dauern
Old-School-Vordergrundbilder können jede CSS- oder JS-basierte Lösung schlagen
Die Ergebnisse können variieren, also schauen Sie sich unbedingt die Zeitlinien für Ihre Situation an.
Ein weiteres kleines Detail: Es wird präziser und genauer, wenn Ihr großes und kleines Bild die gleichen Proportionen haben, um Animationssprünge und Bildverschiebungen während des Übergangs zu vermeiden. In meinen Projekten verwende ich eine durchschnittliche Hintergrundfarbe, um die erste Phase des Ladens abzudecken (vor dem kleinen Bild), aber in Ihrem Fall (Inline-CSS) ist dies nicht notwendig.
Wie berechnet man die Durchschnittsfarbe eines Fotos?
Danke!
Versuchen Sie es mit Color Theif. Das ist eine kleine JS-Bibliothek, um z. B. die dominante Farbe aus einem Bild zu extrahieren.
Vielen Dank dafür!
Für das kleine Bild können Sie PNG verwenden, es wird weniger Platz beanspruchen als JPEG bei winzigen Größen.
Ja, ich habe viel mit verschiedenen Formaten experimentiert, aber JPEG war bei diesem immer noch das beste „Preis-Leistungs-Verhältnis“, selbst bei der kleineren Größe (und nach der Bildoptimierung).
Nicht unbedingt. Und in diesem Fall, Nein.
Das winzige Bild (40×22) in PNG wiegt 1,78 KB. In JPG, 473B bei 35% Qualität.
Wenn das Bild weniger und flachere Farben verwenden würde, wäre das sicherlich eine Möglichkeit.
HINWEIS: Stellen Sie sicher, dass Sie die Leistung testen, insbesondere auf Mobilgeräten. Android-Geräte haben eine relativ schlechte CSS-Leistung, insbesondere vor Android v5. Das Weichzeichnen eines Hintergrundbilds über die gesamte Breite beeinträchtigt die Rendergeschwindigkeit. Stellen Sie also sicher, dass Sie die Kompromisse kennen, bevor Sie es anwenden.
Das gesagt, wenn das Gerät es bewältigen kann, ist der Effekt großartig :)
Toller Artikel! Ich habe kürzlich über die Technik von Medium gepostet, um dies zu tun. In ihrem Fall verwenden sie eine Canvas, um den blur()-Effekt anzuwenden.
Was die winzigen, optimierten Bilder angeht: Wenn Webp eine Option ist, empfehle ich dringend, es sich anzusehen. WebP erzeugt mit der höchsten Komprimierungsstufe Bilder, die 75 % kleiner sind als das Äquivalent mit JPG bei maximaler Komprimierung, zumindest wenn es auf diese winzigen Bilder angewendet wird. Ich habe einige Nachforschungen auf https://jmperezperez.com/webp-placeholder-images/ durchgeführt.
Wenn Sie dies mit Bildern im Body verwenden, im Gegensatz zu nur dem Header, bedeutet dies, dass die Bilder nie tatsächlich geladen werden, wenn Sie etwas zum Offline-Lesen speichern (z. B. in der Leseliste von Safari). Bitte denken Sie zweimal nach, bevor Sie diese Technik anwenden, insbesondere wenn die Bilder für das Verständnis des Inhalts entscheidend sind.
Schön, ich frage mich, wie performant es auf Geräten mit geringerer Leistung ist, ich stelle mir vor, dass das Animieren des Weichzeichnerfilters ziemlich ressourcenintensiv ist.
Es könnte performanter sein, zwei divs zu haben, eines mit dem kleinen Inline-Bild (hinten) und eines mit dem großen Bild (vorne). Das große Bild hat
opacity: 0, wenn das große Bild geladen ist, wechselt es zuopacity: 1. Der Effekt wäre ähnlich (vielleicht nicht ganz so schön), aber ich stelle mir vor, dass die einfachere Operation auf leistungsschwachen Geräten viel besser funktionieren würde. Es würde auch in IE und älteren Android-Browsern funktionieren (im Gegensatz zufilter()).LIEBE dieses Konzept und werde es vielleicht auf der Portfolio-Website meines Studios sowie auf einer oder mehreren Kunden-Websites mit großen Hintergrundbildern verwenden.
Ich möchte jedoch darauf hinweisen, dass die Demo in meiner Umgebung (auf einem 1,7-GHz-rMBP und einer 10-Mbit/s-Verbindung) in keinem Browser gut funktionierte, selbst wenn ich sie ohne Cache zwangsweise lud. Auf Safari springt sie direkt zum Vollbildbild; auf FF und Chrome wird zunächst die verwaschene Version angezeigt, springt dann aber ohne Übergang direkt zum Vollbildbild. 120.000 scheint heutzutage lächerlich wenig für ein großes Hintergrundbild. Um sicherzustellen, dass die Demo für Leute mit guter Breitbandverbindung funktioniert, sollten Sie vielleicht auf mindestens 500.000 hochgehen.
Zum Thema bessere Browserunterstützung: Es ist nicht ganz so flüssig, aber könnten Sie nicht einen zweiten Div über dem eigentlichen Header erstellen, in dem der verwaschene Hintergrund sitzt? Dann, sobald Sie das eigentliche Bild per Lazy-Load geladen und platziert haben, verwenden Sie
opacity, um das verwaschene Bild auszublenden? Es wäre idealer, den Weichzeichner-Effekt ausblenden zu können, anstatt direkt zu einem nicht verwaschenen Bild zu wechseln, aber diese Lösung ermöglicht es, dass der Übergang bei einer viel größeren Vielfalt von Browsern funktioniert.Ich hasste die Sache auf Medium. Es ist das einzige Irritierende an Medium.
Jetzt werde ich das auf vielen Websites sehen..
Meh!!
Das ist eine großartige Lektüre, danke fürs Teilen Emil.
Das winzige 40x22-Bild, das Sie für ca. 1 KB haben, kann um weitere 50 % auf ca. 473 B reduziert werden.
Sie können es hier sehen/herunterladen: http://imgur.com/rke9rgE
Und wissen Sie, womit ich es komprimiert habe ... die beste Bildkomprimierungsmaschine, die jemals erstellt wurde: Adobe Fireworks ;)
Für alle, die WordPress verwenden und diese Technik nutzen möchten, haben wir eine ähnliche Lösung implementiert, um dies automatisch als WordPress-Plugin hier zu tun: https://wordpress.org/plugins/featured-image-sharpen-up/
Ich bin mir nicht sicher, ob Facebook die ganze Ehre dafür beanspruchen kann. Google Maps verwendet diese Technik seit ihrer Erstellung häufig. (Mehr bei Satellitenbildern und noch mehr bei Street View)
Leider scheint Ihre SVG-Lösung auf allen Versionen von IE + Edge zu fehlschlagen, aber aus Gründen, die ich nicht ganz nachvollziehen kann.
Sehen Sie, das SVG funktioniert wie erwartet, wenn es in einem img (src="data:...") verwendet wird, aber wenn es als Hintergrundbild verwendet wird, wird es erst angezeigt, wenn die Hintergrundgröße 1280px oder weniger beträgt. Warum 1280? Keine Ahnung.
Nachdem ich damit herumgespielt habe, sieht es so aus, als ob das Problem beim Filter liegt. Wenn der Filter aus dem SVG entfernt wird, wird das Hintergrundbild gerendert, aber natürlich ohne Weichzeichnung, was der eigentliche Sinn der Sache ist.
Oh Gott, ich hatte gerade eine schreckliche Idee. Ich könnte immer eine Lösung basteln, bei der ich ein Bild absolut über einem Div positioniere, um ein Hintergrundbild zu simulieren.
Eigentlich, nachdem ich über meine schreckliche Idee nachgedacht habe... sie ist gar nicht so schrecklich.
Man könnte das SVG-Bild über dem Div positionieren, mit allen Inhalten im Div relativ positioniert und mit einem höheren z-index + Opazität als das SVG.
Dann, sobald das große Bild geladen ist, fügen Sie dieses Bild als Hintergrundbild zum Div hinzu und reduzieren dann die Opazität des SVG mit einem Übergang auf 0, wodurch der Weichzeichner-Effekt umgekehrt wird, was in allen Browsern funktioniert, die SVG-Filter und Opazität unterstützen (IE10+?).
Oder ich könnte einfach ... das alles nicht tun und in einer Ecke weinen.
Hallo Eli, danke für das Feedback. Ich sehe keines der von Ihnen beschriebenen Probleme in IE oder Edge – getestet auf verschiedenen Versionen, Bildschirmgrößen und Betriebssystemen (saubere virtuelle Maschinen von modern.ie). Meistens funktioniert es gut, aber natürlich ohne die Animation.
Am ehesten tritt auf, dass das verwaschene Bild manchmal nicht angezeigt wird, bevor das scharfe Bild geladen wird, aber ich kann das nicht zuverlässig reproduzieren.
Ich vermute, dass die Leistung des SVG-Filters bei größeren Ansichtsfenstergrößen unter IE schlecht ist, sodass er nicht genug Zeit hat, die Weichzeichnung zu rendern, bevor das scharfe Bild geladen wird. Vielleicht. Dies *ist* eine Proof-of-Concept-Technik (zumindest in meinen Augen), daher kann die Leistung von Browser zu Browser stark variieren :-)
Es ist keine Leistungssache. Das SVG wird nicht als Hintergrund gerendert, wenn es jemals größer als 1280 Pixel ist.
Probieren Sie diesen Pen aus, modifiziert, so dass die JS das große Bild nie tatsächlich hinzufügt.
http://codepen.io/anon/pen/BjRBXY
Das SVG rendert unter IE+Edge nie, funktioniert aber überall sonst einwandfrei.
Interessant – ich habe gerade noch einmal mit keinem JS unter IE10/Win7, IE10/Win8, IE11/Win7, Edge 12/Win10 und Edge13/Win10 getestet – alle IEs funktionieren auf meinem Rechner absolut einwandfrei (d.h. zeigen das verwaschene SVG bei jeder Bildschirmgröße an), aber ich konnte den 1280-Pixel-Bug in beiden Edge-Versionen reproduzieren.
Klingt wie ein saftiger Bug für das Edge-Team, ich werde ihnen einen Bericht schicken. :-)
Es könnte ein Windows 10-Ding sein. Nach weiteren Tests ist ie11 unter Win 7 in Ordnung, aber ie11 unter Windows 10 hat das 1280-Problem.
(Entschuldigen Sie den Doppelpost. Ich habe versehentlich eine neue Antwort unten erstellt, aber Sie erhalten möglicherweise keine Benachrichtigung.)
Es könnte ein Windows 10-Ding sein. Nach weiteren Tests ist ie11 unter Win 7 in Ordnung, aber ie11 unter Windows 10 hat das 1280-Problem.
Leser Ryvan Prabhu schreibt
https://github.com/ryvan-js/bleach
Wout Mertens schreibt