Auf Twitter stellte Paulina Seroczynska die Frage, ob man ein Bild in TYPO3 2x rendern kann um eine optmierte Version für HiDPI-Displays zur Verfügung zu stellen.
tl;dr
ja kann man. Hier das Snippet:
###Retina### lib.retinaSnippet = COA lib.retinaSnippet { 1 = TEXT 1.value = <figure class="csc-textpic-imagecolumn###CLASSES###" 20 = IMG_RESOURCE 20 { file.import.current = 1 file.maxW.cObject = TEXT file.maxW.cObject.data = TSFE:lastImageInfo|0 file.maxW.cObject.dataWrap = |*2 file.maxW.prioriCalc = intval stdWrap.noTrimWrap = | data-retina="|" | } 1000 = TEXT 1000.value = >|###CAPTION###</figure> } # insert into tt_content.image.20.rendering |
Gern etwas genauer… 😉
Retina? Gibts da nicht schon etwas im TER
Nach einer kleinen Diskussion auf Twitter kam der Einwurf, dass es doch da was im TER gibt…
Nachdem ich die TYPO3-Extension „Retina“ probiert habe und nicht so glücklich damit war, hab ich probiert, was mit Boardmitteln in TYPO3 möglich ist, also eine Ausgabe von Retina-Bilder nur über TypoScript zu ermöglichen.
Retina Images – ein kurzer Exkurs
Mit dem Begriff „Retina Images“ bezeichnet man Bilder, die für hochauflösende Display optimiert sind. Das sind mittlerweile schon weit mehr Displays, als es bei der Einführung des iPhone 4 und der ersten Nennung des Begriffes waren. Aktuelle Smartphones wie das Samsung Galaxy SIII, Samsung Note, das Nexus 4 von LG aber auch Tablets wie Googles Nexus 10 sind mit Displays ausgestattet, die eine doppelte Pixeldichte haben. Viele ältere Android-Smartphones haben bereits ein Display, das mit einer 1,5-fachen Pixeldichte aufwartet. Dazu zählen Geräte wie das Motorola Milestone (Droid), Samsung Galaxy S2 oder das HTC Desire.
Inhalte wie Schrift oder Skalierbare Vektor Grafiken (SVG) werden sehr scharf dargestellt, normale Bilder wirken aber pixelig und unscharf.
Retina Bilder zur Verfügung stellen
Ein verbreiteter Trugschluss einfach die DPI-Einstellung im Photoshop hoch zu stellen, funktioniert nur im Print-Umfeld. Browser richten sich lediglich nach der Dimension des Bildes, also Breite des Bildes in Pixel und nicht nach der Pixeldichte. Bilder müssen also in doppelter Dimension eingebunden werden, aber nur halb so groß (also so groß wie das normale Bild) angezeigt werden.
Viele Tutorials im Netz beschreiben, dass man ein normales Bild (bild.jpg) und ein hochauflösendes Bild mit dem Suffix ‚@2x‘ also z.B. bild@2x.jpg für HiDPI optimierte Webseiten bereit stellen sollte. Der Suffix ‚@2x‘ rührt von der Umsetzung in iOS, da dort automatisch das hochauflösende Bild in einer App verwendet wird, wenn es den Suffix ‚@2x‘ enthält. Das funktioniert im Browser leider nicht. Obwohl… in Safari 6 und seit Chrome 21 gibt es mittlerweile eine Implementierung einer ähnlichen Idee.
.selector { background-image: url(fallback.jpg); background-image: -webkit-image-set( url(test-normal.jpg) 1x, url(test-high.jpg) 2x); } |
Etwas genauer kann man das im Cloud Four Blog nachlesen.
Dieser Ansatz ist leider noch nicht Cross-Browser einsetzbar. Andere Lösungen müssen also her und der aktuelle Stand des <picture>-Element für HTML5 Responsive Images ist auch erst im Anfangsstadium.
Scripte wie die retina.js Library haben sich der Problematik angenommen. Das Script schaut beim Aufruf der Seite ob ein Bild mit dem Suffix ‚@2x‘ auf dem Server vorhanden ist und ersetzt dann das Originalbild. Das Script macht dies bereits für Screens, die eine 1,5-fache Pixeldichte haben.
Der Nachteil ist ganz klar, Bilder werden zwei Mal geladen, erst die normalen und dann die Retina-Bilder.
Einbindung in TYPO3
In TYPO3 können wir den Suffix ‚@2x‘ leider nicht automatisch an die Bilder anhängen, die automatische Generierung der benötigten Bilder lässt dies nicht zu (ich weiß jedenfalls nicht wie).
Anhand einer einfachen TypoScript-Condition ein doppelt so großes Bild als Resource zu verknüpfen geht leider auch nicht, da die Attribute width=““ und height=““ von TYPO3 automatisch gesetzt werden. Bedeutet also ein Bild das 400x300px angezeigt werden soll, aber 800x600px groß ist, wird mit der eigentliche Größe ins HTML eingebunden, also width=“800″ height=“600″.
Jetzt könnte man anhand einer Klasse, die á la Modernizr per JavaScript ins HTML-Element geschrieben wurde, die Bilder per CSS wieder um 50% verkleinern, blablabla, ist aber eigentlich viel zu kompliziert und unnötig.
Eine Möglichkeit ist es, das generierte Bild per HTML5 data-Attribut an ein Wrapper-Element zu binden und mit JavaScript die Source zu switchen, wenn der Browser ein HiDPI-Screen erkennt. Keine optimale Lösung, da auch hierbei zwei Bilder geladen werden, aber eine einfache Lösung mit TYPO3-Boardmitteln. Nochmal das Snippet etwas genauer:
###Retina### lib.retinaSnippet = COA lib.retinaSnippet { 1 = TEXT 1.value = <figure class="csc-textpic-imagecolumn###CLASSES###" 20 = IMG_RESOURCE 20 { file.import.current = 1 file.maxW.cObject = TEXT file.maxW.cObject.data = TSFE:lastImageInfo|0 file.maxW.cObject.dataWrap = |*2 file.maxW.prioriCalc = intval stdWrap.noTrimWrap = | data-retina="|" | } 1000 = TEXT 1000.value = >|###CAPTION###</figure> } |
Wir generieren ein Content Object Array (COA), das mehrere Teile hat. Teil 1 ist ein TEXT-Objekt, das unseren Wrap eröffnet. Der Teil ist aus dem bisherigen Rendering kopiert.
1.value = <figure class="csc-textpic-imagecolumn###CLASSES###" |
Im zweiten Teil des Snippets verwenden wir IMG_RESOURCE um das Bild, dass im TYPO3-Backend angegeben ist, zu holen und neu zu prozessieren.
20.file.import.current = 1 20.file.maxW.cObject = TEXT |
Die Breite des zu generierenden Bildes errechnen wir anhand der bisherigen Größe.
20.file.maxW.cObject.data = TSFE:lastImageInfo|0 |
Die empfangene Zahl wird nun mit der Angabe (dataWrap = |*2) erweitert.
20.file.maxW.cObject.dataWrap = |*2 |
Im Anschluss wird diese „Zahlenkombination“ durch eine mathematische Funktion geschleust, die eine Ganzzahl zurückgibt.
20.file.maxW.prioriCalc = intval |
Zu guter Letzt wird der generierte Bildpfad mit „data-retina“ gewrappt und im dritten Teil des Snippets wieder geschlossen.
20.stdWrap.noTrimWrap = | data-retina="|" | |
1000.value = >|###CAPTION###</figure> |
Das Snippet fügen wir nun jeweils in das Rendering der tt_content.image Variationen ein. Der „wrap“, den wir generieren umschließt also das normaleBild.
tt_content.image.20 { rendering { singleNoCaption { singleStdWrap.wrap.override.cObject < lib.retinaSnippet } noCaption { singleStdWrap.wrap.override.cObject < lib.retinaSnippet } singleCaption { singleStdWrap.wrap.override.cObject < lib.retinaSnippet } splitCaption { singleStdWrap.wrap.override.cObject < lib.retinaSnippet } globalCaption { singleStdWrap.wrap.override.cObject < lib.retinaSnippet } } } |
Um TYPO3 allgemein für Responsive Webdesign vorzubereiten, sind allerdings noch ein paar Schritte notwendig, die ich in der aktuellen T3N beschrieben habe.
Jetzt noch eine kleine Änderung ins CSS einbinden und (der Einfachheit halber) eine simple jQuery-Funktion für den Bilder-Switch einbinden.
<style> /* insert snippet for responsive images here */ [data-retina] { max-width: 100%; } </style> |
<script> /* load jQuery */ if(window.devicePixelRatio !== undefined) && (window.devicePixelRatio >= 1.5) { $("[data-retina]").each(function() { $('img', $(this)).attr('src', $(this).attr('data-retina')); }); }; </script> |
Häufig wird zudem der Hinweis gegeben, dass die HTML-Attribute width=““ & height=““ im Responsive Webdesign entfernt werden sollten. Das muss nicht sein, das regelt CSS schon und hat den Vorteil, das beim Laden der Seite der Platz für Bilder schon einberechnet wird. Zudem werden unsere Retina Images genau eingepasst, ohne zusätzlich mit CSS nachzuhelfen.
Wie schon gesagt, das ist eine einfache Lösung mit TYPO3-Boardmitteln, optimaler wäre natürlich eine Option in TYPO3. Habt ihr andere Lösungen, Ideen oder Hinweise zur Verbesserung, schreibt einfach einen Kommentar.