REMux: Ein experimenteller Ansatz für responsives Webdesign

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Beitrag ist ein Gastbeitrag von Dirk Lüth. Dirk hatte eine interessante Auffassung von responsivem Layout. Er verwendet Media-Query-Breakpoints, um Dinge wie gewohnt zu verschieben, aber die fließenden Spaltenbreiten werden in rem-Einheiten statt in Prozent festgelegt, und die font-size wird dynamisch mit JavaScript basierend auf der Bildschirmbreite angepasst. Das Ergebnis ist ein gewisser "Zoom"-Effekt, nach oben oder unten, bis die nächste Media Query greift. Ich überlasse es ihm, Ihnen mehr darüber zu erzählen.

Einführung

Ich bin sicher, wir sind uns alle einig, dass responsives Webdesign eines der größten Themen der letzten Jahre war und mit dem Wachstum von Mobilgeräten weiter zunehmen wird. Als erfahrener Front- und Backend-Entwickler mit starkem Interesse an Forschung und Entwicklung in meinem Unternehmen bin ich dafür verantwortlich, Techniken wie RWD zu evaluieren. Wann immer ich einen Link zu einem völlig neuen CSS-Grid-System erhielt, wurde ich immer skeptischer. Sie fühlten sich für mich nicht "richtig" an, aber ich war mir nicht sicher warum.

Dann stieß ich zufällig auf einen großartigen Artikel von Ian Yates mit dem Titel "Life Beyond 960px: Designing for Large Screens", der mich mit dem Begriff "Screen Real Estate" vertraut machte. Davor habe ich tiefere Recherchen mit rem-Einheiten in CSS durchgeführt, was ein glücklicher Zufall war. Plötzlich wusste ich, was sich falsch anfühlte.

Wenn wir über RWD sprechen, reden wir meistens über Geräte unterhalb der Zielbreite unserer Layouts. Aber was ist mit größeren Bildschirmen? Die meisten von Ihnen werden zustimmen, dass eine nicht-RWD-Website mit einer Zielbreite von 960 Pixeln auf einem solchen Bildschirm etwas seltsam oder verloren aussieht. Die Dinge werden offensichtlicher, wenn wir über Leute sprechen, die mit einem 60-Zoll-Fernseher auf unsere Websites zugreifen. Sicher, diese Fernseher werden wahrscheinlich immer noch nur eine Full-HD-Auflösung haben. Aber denken Sie daran, dass jeder, der davor sitzt, wahrscheinlich mindestens 4 Meter vom Bildschirm entfernt ist.

Aktuelle Situation

Ob wir zuerst mobil, Tablet oder Desktop zuerst entwickeln – die meisten von uns werden am Ende mindestens 3 Media-Query-Breakpoints mit unterschiedlichen Layouts haben. Oder wir werden ein Grid-System verwenden, das die Zusammensetzung der Inhaltselemente unserer Seiten automatisch ändert. Oder eine Kombination aus beidem. Beide Ansätze haben ihre Nachteile, wenn wir immer mehr verschiedene Auflösungen und verschiedene Betrachtungssituationen unterstützen wollen.

  • Mehr Breakpoints = mehr Layouts = mehr Arbeit
  • Schwierig, Flexibilität und Proportionen von Elementen auszubalancieren
  • Ruckartige Neuanordnung von Elementen
  • Begrenzt durch die Menge des Inhalts, um den Viewport auszufüllen
  • Die Kunst des Grids ist mehr als nur einige Hilfslinien in Photoshop

Und wie auch immer wir uns entscheiden, für am passendsten zu halten: Der eigentliche Inhalt (einschließlich jedes einzelnen Elements) skaliert nicht proportional.

Proof of Concept

Was mir in den Sinn kam, war die Idee eines rein rem-gesteuerten Layouts basierend auf font-size. Wenn diese Idee funktionieren würde, könnte ich ganz einfach alle Inhalte skalieren, indem ich einfach die font-size des <html>-Elements ändere. Dieses Konzept würde die größte Herausforderung lösen: Layouts skalieren nahezu perfekt innerhalb ihrer Grenzen.

Denken Sie daran, dass rem erst ab IE9+ und allen anderen aktuellen Browsern unterstützt wird. Für ältere Browser ist ein px-basierter Fallback möglich. Sehen Sie sich die Beispiel-Mixins unten an.

Ich begann mit einigen Proof-of-Concept-Arbeiten auf meiner eigenen Website. Die statische Version funktionierte sehr gut, nachdem ich einige LESS-Mixins entwickelt hatte, die Pixel-Einheiten (direkt aus dem Layout übernommen) in rem-Einheiten umwandeln, basierend auf der von mir gewählten font-size von 14px (Beispiel ansehen).

LESS Mixins

Es gibt zwei Arten von Mixins hier: eine für Eigenschaften mit einzelnen Parametern (wie font-size) und eine weitere für Eigenschaften mit mehreren Parametern (wie border). Beide basieren auf zwei LESS-Variablen, die definiert werden müssen.

@remuxFontsize: 14px;  // base font size
@remuxFallback: false; // switch to include px fallback

.remux(font-size, @value) {
  @processingRem: ((round((@value / @fontsize-base) * 10000000)) / 10000000);

  .result(@value, @fallback) when (@fallback = true) {
    font-size: ~"@{value}px";
    font-size: ~"@{processingRem}rem";
  }
  .result(@value, @fallback) when (@fallback = false) {
    font-size: ~"@{processingRem}rem";
  }

  .result(@value, @remuxFallback);
}

.remux(border, @value) {
  @processingRem: ~`(function() { var base = parseFloat("@{remuxFontsize}"); return "@{value}".replace(/[\[\]]/g, '').replace(/([0-9]*[.]{0,1}[0-9]*px)/gi, function() { return ((Math.round((parseFloat(arguments[0]) / base) * 10000000)) / 10000000) + 'rem'; }); }())`;

  .result(true) {
    border: @value;
    border: @processingRem;
  }
  .result(false) {
    border: @processingRem;
  }

  .result(@remuxFallback);
}

Die Verwendung der Mixins ist ziemlich einfach

/* Instead of this... */
font-size: 16px;
/* You use this */
.remux(font-size, 16px);

/* Instead of this... */
border: 7px solid #000;
/* You use this */
.remux(border, ~"7px solid #000");

Als ich die CSS meiner Website auf rem umstellte, konnte ich bereits die font-size des <html>-Elements über die Entwicklertools ändern und sehen, wie die Website fast perfekt skaliert!

Was ich als Nächstes brauchte, war eine Lösung, um dies dynamisch über JavaScript bei "resize"- und "orientationchange"-Ereignissen zu berechnen. Die grundlegende Berechnung ist wirklich einfach

;(function(window, document, undefined) {
  'use strict';
  
  var targetLayoutWidth = 980,
    baseFontsize      = 14,
    htmlElement       = document.getElementsByTagName('html')[0],
    documentElement   = document.documentElement;
  
  function updateFontsize() {
    var currentFontsize = baseFontsize * (documentElement.offsetWidth / targetLayoutWidth);
      
    htmlElement.style.fontSize = currentFontsize + 'px';
  }
    
  window.addEventListener('resize', updateFontsize, false);
  window.addEventListener('orientationchange', updateFontsize, false);
  
  updateFontsize();
}(window, document));

Beim Setzen der resultierenden font-size stellte ich fest, dass Gleitkommazahlen Probleme verursachen, wenn sie vom Browser gerundet werden (Beispiel ansehen). Also musste ich schließlich die font-size mit floor abrunden, was jegliche Rundungsfehler eliminierte, aber das Skalieren weniger "flüssig" machte. Für mich ist es aber immer noch mehr als ausreichend (Beispiel ansehen).

Als Nächstes habe ich einige erweiterte Tests auf meinem iPad 2 mit dem HTML-Viewport auf "device-width" durchgeführt. Ich gebe zu, ich war etwas überrascht, als nicht nur alles nach dem anfänglichen Seitenaufruf funktionierte, sondern auch Pinch-to-Zoom und Änderungen der Geräteausrichtung wie gewünscht funktionierten.

Implementierung von Layouts

Nun, da ich mein Layout unendlich skalieren konnte, begann ich mit der Implementierung von Breakpoints. Breakpoints werden weiterhin benötigt, da das Herunterskalieren der font-size auf Null oder das Hochskalieren ins Unendliche nicht wirklich sinnvoll ist (obwohl technisch möglich).

Ich begann mit der Planung der Struktur meines zukünftigen Layout-Objekts, indem ich bestimmte, was erforderlich war. Dies führte zur folgenden JavaScript-Objektstruktur

var layouts = {
  'ID': {
    width:      980,            // designs target width
    base:       14,             // designs base font-size
    min:        10,             // minimum font-size
    max:        18,             // maximum font-size
    breakpoint: 980 * (10 / 14) // minimum breakpoint
  }
}

Nun, da Layouts definiert werden konnten, musste ich sie zur Update-Funktion hinzufügen, die mehr Auswirkungen hatte, als ich dachte (Beispiel ansehen).

;(function(window, document, undefined) {
  'use strict';

  var htmlElement     = document.getElementsByTagName('html')[0],
    documentElement = document.documentElement,
    layouts = {
      mobile: {
        width:      420,
        base:       14,
        min:        10,
        max:        23,
        breakpoint: 420 * (10 / 14)
      },
      desktop: {
        width:      980,
        base:       14,
        min:        10,
        max:        18,
        breakpoint: 980 * (10 / 14)
      }
    },
    state = {
      size:   null,
      layout: null
    };

  function updateFontsize() {
    var width, id, layout, current, size;
  
    width = documentElement.offsetWidth;
  
    for(id in layouts) {
      if(layouts[id].breakpoint && width >= layouts[id].breakpoint) {
        layout = id;
      }
    }
  
    if(layout !== state.layout) {
      state.layout = layout;
  
      htmlElement.setAttribute('data-layout', layout);
    }
  
    current = layouts[state.layout];
  
    size = Math.max(current.min, Math.min(current.max, Math.floor(current.base * (width / current.width))));
  
    if(size !== state.size) {
      state.size = size;
  
      htmlElement.style.fontSize = size + 'px';
    }
  }
  
  window.addEventListener('resize', updateFontsize, false);
  window.addEventListener('orientationchange', updateFontsize, false);
  
  updateFontsize();
}(window, document));

Ich habe diesen Prototyp in Chrome, Safari, FF17+ und IE9 (wo REMux funktionieren sollte) getestet, und er funktioniert.

In Aktion sehen

Ich habe bereits erwähnt, dass REMux stark auf meiner eigenen Website (über-)beansprucht wird. Daneben gibt es einige Pens auf CodePen, die im gesamten Artikel verlinkt wurden. Zusätzlich gibt es einen weiteren Pen, der zeigt, was ich am Ende hatte.


Ich hoffe, bald etwas Dokumentation fertig zu haben – aber der Code ist so, wie er ist, nicht schwer zu lesen.

Was kommt als Nächstes?

REMux funktioniert so gut, dass ich beschlossen habe, es zu einem Teil meiner wachsenden JavaScript-Bibliothek zu machen, die auf GitHub zu finden ist. In den letzten Wochen habe ich auch einige weitere Funktionen hinzugefügt, die in diesem grundlegenden Artikel fehlen

  • AMD-kompatibel (require.js)
  • addLayout()-Methode zum Hinzufügen von Layouts, ohne das Skript zu ändern
  • Verhältnisse für Pixeldichte, Zoomstufe, Schriftgröße, gesamt und Bilder
  • getState()-Methode, die alle Größen und Verhältnisse zurückgibt
  • Sendet Ereignisse ratiochange und layoutchange mit dem aktuellen Zustand als Argument
  • Schutzmechanismus gegen Flut für wiederholte Eingabeereignisse für bessere Leistung
  • Erweiterbar durch einen Objekt-Erweiterungsmechanismus

Die Standalone-Version von REMux finden Sie im Ordner "packages" meines GitHub-Repositorys und wiegt minimiert und komprimiert etwa 4 KB. Es hängt nicht von jQuery oder einer anderen größeren Bibliothek ab. Seine Abhängigkeiten zu anderen Komponenten meiner Bibliothek sind alle im Paket enthalten. Alle Komponenten meiner JavaScript-Bibliothek sind dual lizenziert unter der MIT- und GPL-Lizenz.

Fühlen Sie sich frei, es herunterzuladen und in Ihren Projekten zu verwenden, aber denken Sie daran, Feedback zu geben und Fehler zu melden, damit ich es noch besser machen kann!