Erstellen Sie eine mobile Vorlage mit Lenkern

Autor: Peter Berry
Erstelldatum: 18 Juli 2021
Aktualisierungsdatum: 13 Kann 2024
Anonim
🏆🥈 НОВАЯ КОЛЛЕКЦИЯ УЗОРОВ №2!!! ВИДЕО ОБЗОР УЗОРОВ КРЮЧКОМ  (вязание крючком для начинающих)
Video: 🏆🥈 НОВАЯ КОЛЛЕКЦИЯ УЗОРОВ №2!!! ВИДЕО ОБЗОР УЗОРОВ КРЮЧКОМ (вязание крючком для начинающих)

Inhalt

"Mobil": ein so interessantes und umfassendes Wort. Es scheint heutzutage, dass es entweder das Hauptgesprächsthema zwischen Webdesignern und Entwicklern ist oder zumindest tangential miteinander verbunden ist. So wie sich die Schlagworte "HTML5" und "Ajax" durchgesetzt haben, hat die mobile Nutzung weiter an Bedeutung gewonnen. Es ist vielleicht nicht überraschend, dass der gleiche Ansatz zum Erstellen des Frontends einer Ajax-gesteuerten Site für einen Desktop-Browser auch auf eingebettete HTML5-Apps angewendet werden kann, die auf Mobilgeräten ausgeführt werden können.

Dies ist das Thema meines Vortrags auf der Big Design Conference in Dallas, TX - Verwenden Sie Web-Fähigkeiten, um mobile Apps zu erstellen. Zufälligerweise (oder bequemerweise für mich) ist es auch das, was ich in diesem Artikel behandeln werde: Wie man Handlebars.js verwendet, um eine leichte HTML5- "App" zu erstellen, die die Dribbble-API verwenden kann. Klingt gut? Okay, dann lass uns loslegen.

Die Grundlagen

Handlebars.js ist eine Obermenge von Moustache, einer logiklosen Vorlagensyntax, die in eine Vielzahl von Programmiersprachen integriert werden kann. Die Prämisse hinter Moustache ist zwar bewundernswert - die vollständige Trennung von Geschäftslogik und Präsentation -, kann jedoch häufig sehr einschränkend sein und separate Vorlagen erfordern, unabhängig davon, ob bestimmte JSON-Eigenschaften zum Rendern verfügbar sind oder nicht.


Für mich bietet der Lenker die perfekte Balance zwischen Leichtgewicht, Entschlüsselbarkeit in HTML und einer Logik, die gerade ausreicht, um das DRY-Prinzip (Don't Repeat Yourself) einzuhalten.

Wie bei Moustache verwendet Lenker "{{" und "}}", um JSON-Eigenschaften zu kapseln. Seitlich gedreht, können diese geschweiften Klammern wie ein Schnurrbart aussehen (daher das Wortspiel auf "Lenkerschnurrbart"). Über den Schnurrbart hinaus enthält der Lenker die Syntax, die für grundlegende Bedingungen erforderlich ist, wie z.

p> {{#if etwas}} Mach Sachen mit {{etwas}} {{else}} Entschuldigung, wir haben nichts. {{/ if}} / p>

Anhand dieses Codebeispiels ist es nicht schwierig zu bestimmen, was passiert. Es erinnert ein wenig an das Aussehen der ExpressionEngine-Syntax oder an den Templating-Ansatz in Django. Es ist wesentlich menschlicher lesbar (und designerfreundlich) als die Verkettung von Zeichenfolgen der alten Schule in einer JavaScript-Schleife. Apropos, Lenker führt auch Schleifen durch, sodass ein einfaches HTML-Snippet für eine Liste von Elementen wiederverwendet werden kann.


{{#if items}} ul> {{#each items}} li> {{this.name}} / li> {{/ each}} / ul> {{/ if}}

Lenker können auch mehrstufige JSON-Objekte ausführen. Hier ist ein Beispiel für ein hypothetisches Restaurantmenü. Im vorherigen Beispiel hatten wir {{this.name}}, aber im nächsten Beispiel lassen wir das "this" weg. Präfix. Ohne funktioniert es einwandfrei, da Lenker wissen, was {{name}} bedeutet, wenn sie kontextuell in {{#each}} verschachtelt sind.

{{#mit Menü}} dl> {{#jede Kategorien}} dt> {{Name}} / dt> dd> {{#jede Unterkategorien}} dt> {{Name}} dt> {{#jedes Element} } dd> {{name}} - {{price}} / dd> {{/ each}} {{/ each}} / dd> {{/ each}} / dl> {{/ with}}

Abhängig von den übergebenen JSON-Daten würde dies ungefähr so ​​aussehen…


dl> dt> Getränke / dt> dd> dt> Weine in der Flasche / dt> dd> Kristallweißwein - $ 20,95 / dd> dd> Rotwein, Rotwein - $ 25,00 / dd> dt> Limonaden / dt> dd> Cola - $ 1,99 / dd> dd> Selters - $ 1,99 / dd> / dd> dt> Entres / dt> dd> dt> Meeresfrüchte / dt> dd> usw. / dd> / dd> / dl>

Demo-App

Nachdem wir einige der einführenden Grundlagen behandelt haben, wollen wir uns mit der Demo befassen. Für die Zwecke dieser Diskussion habe ich mich ausschließlich auf die Front-End-Aspekte konzentriert, während für eine tatsächliche Site zweifellos einige serverseitige Komponenten beteiligt wären.


Der Grund, warum ich diesen Weg gegangen bin, ist zweierlei. Erstens die Kürze. Zweitens hat man beim Erstellen einer HTML5-App, die in ein mobiles Gerät eingebettet werden soll, nicht den Luxus einer serverseitigen Umgebung, die auf dem Telefon ausgeführt wird. Die Art und Weise, wie wir unsere Daten in die HTML-Ansicht übertragen, besteht darin, JSON von / an einen Remote-API-Endpunkt zu empfangen (und optional zu senden). In diesem Fall Dribbble. Da diese spezielle API schreibgeschützt ist, werden wir einfach Daten abrufen und in unserer HTML-Ansicht rendern.


Aus Mangel an einem besseren Namen habe ich diese Demo-App "Handlebbbars" genannt, da wir Handlebars.js verwenden, um die Dribbble-API zu verwenden. Ich verwende Sass (über Compass), um die Stylesheets zu schreiben, die dann in eine einzelne application.css-Datei kompiliert werden. Meiner Meinung nach ist dies der beste Workflow für die CSS-Vorverarbeitung. Es würde den Rahmen dieses Artikels sprengen, aber wenn Sie neugierig sind, habe ich hier darüber geschrieben ...

Schauen wir uns die Demo an.html und application.js Dateien.

demo.html

Das erste bemerkenswerte Element in demo.html ist der Wählen Sie> Dropdown-Menü mit "Seite 1" bis "Seite 25" (die Dribbble-API bietet bis zu 25 Seiten mit jeweils 30 Aufnahmen). Dies dient zwei Zwecken:

1. Es ist ein visueller Indikator dafür, welche Seite angezeigt wird.


2. Es kann verwendet werden, um direkt zu einer bestimmten Seite zu springen.

Es ist jedoch nicht das nur Navigationsmethode, die wir bereitstellen werden. Apropos Navigation, wir haben auch eine beiseite> mit Anweisungen zum Paginieren entweder über die J / K-Tasten (bei Verwendung eines Desktop-Browsers) oder durch Wischen nach links / rechts auf einem Touchscreen-Gerät.

Darunter haben wir den Kern der gesamten App, ul id = "Liste">. Dies ist einfach ein leerer Container, in den wir unseren dynamisch generierten Inhalt einfügen, nachdem JSON von Handlebars analysiert und in HTML gerendert wurde. Wir sehen auch div id = "Laden"> und div id = "Fehler">. Die Lademeldung wird angezeigt, wenn Ajax (JSONP) -Anforderungen an die Dribbble-API gesendet werden, und ausgeblendet, wenn Daten zurückgegeben werden. Für den Fall, dass die Ajax-Anforderungen zu lange dauern (oder vollständig fehlschlagen), wird die Fehlermeldung angezeigt. Dies bietet dem Benutzer die Möglichkeit, die Seite manuell zu aktualisieren.


Gegen Mitte des Seitencodes wird die Skript> Das Tag, das unsere Vorlage enthält, trägt einen merkwürdigen Inhaltstyp…

script type = "text / x-handlebars" id = "_ template-list-item"> {{#jede Aufnahmen}} li> p> b> {{title}} / b> img alt = "{{title}} "style =" Hintergrundbild: URL ({{image_url}}) "src =" Daten: image / png; base64, iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAEklEQVQIHWP8 // 8 / AzJgJCgAAB + ICPu> / th> td> b> {{player.name}} / b> / td> / tr> {{#if player.twitter_screen_name}} tr> th> Twitter: / th> td> a href = "http: / /twitter.com / {{player.twitter_screen_name}} "> @ {{player.twitter_screen_name}} / a> / td> / tr> {{/ if}} {{#if liks_count}} tr> th> Likes: / th> td> {{lik_count}} span> ♥ / span> / td> / tr> {{/ if}} {{#if short_url}} tr> th> URL: / th> td> a href = " {{short_url}} "> {{short_url}} / a> / td> / tr> {{/ if}} / table> / li> {{/ each}} / script>

Während das meiste davon ziemlich einfach sein sollte, gibt es ein paar Dinge, die ich speziell hervorheben möchte. Das Typattribut auf dem Skript> Tag enthält einen Wert, der für alle Browser unsinnig ist: Text / X-Lenker. Wirklich, das könnte alles sein - wie Typ = "Erdnussbutter" - Solange es sich nicht um Text / Javascript handelt, würde ein Browser den Inhalt als JavaScript analysieren (was wir nicht wollen). Stattdessen wollen wir die Skript> einfach da sitzen und nichts tun. Wir werden die Vorlage später über den Code in abrufen application.js.



Es ist erwähnenswert, das Seltsame src = "Daten: ..." auf dem Vordergrundbild. Ich codiere spacer.png (das finden Sie in der "/ Assets / Bilder" Ordner), da dadurch möglicherweise HTTP-Anforderungen gespeichert werden, wenn dieser HTML-Code als Website bereitgestellt wird. Alternativ könnte unser Code in eine native App eingebettet sein. In diesem Fall wäre die Netzwerklatenz ein strittiger Punkt, weil spacer.png wäre am Telefon selbst.

Als kluger Leser könnten Sie neugierig sein, was spacer.png sieht aus wie. Es ist einfach ein transparentes 4x3-Pixel-Bild. Ich habe dies getan, weil die Bildfläche für Dribbble vorhersehbare 400 x 300 Pixel beträgt. Also, wie die spacer.png Dies zwingt dazu, ein konsistentes 4x3-Seitenverhältnis beizubehalten. Ich lade jede Aufnahme als Hintergrundbild hinter diese transparente Ebene und erzwinge eine optimale Anpassung über - Hintergrundgröße: Auto 100% - Nur für Aufnahmen mit einem funky (nicht 4x3) Seitenverhältnis. Dies hat den zusätzlichen Vorteil des Caching, da die meisten Browser keine zusätzliche HTTP-Anforderung senden, wenn bereits ein Hintergrundbild im Cache vorhanden ist.




Ganz am Ende der Seite haben wir die Skript> Tags, die das gesamte aktuelle JavaScript enthalten. Für IE9 diene ich jQuery und für andere Browser Zepto.

! - [if gt IE 9]>! -> script src = "./ assets / js / zepto.js"> / script>! -! [endif] ->! - [if lte IE 9 ]> script src = "http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"> / script>! [endif] ->

Sie können sich Zepto im Grunde genommen als eine leichtere Version von jQuery vorstellen, bei der die gesamte Browserunterstützung für ältere IE entfernt wurde und die sich mehr auf mobile Geräte (Swipe, LongTap) konzentriert. Ich wollte sicherstellen, dass IE9 unterstützt wird, unterstütze jedoch IE8 oder niedriger nicht, da keine der älteren IE-Versionen auf Mobilgeräten eine nennenswerte Präsenz aufweist. Stattdessen konzentrieren wir uns auf Browser-Engines mit erheblichem Marktanteil: WebKit, Opera und IE9. Wir unterstützen Firefox auch implizit - obwohl es auf Mobilgeräten nicht viel Traktion hat - einfach, weil es ein anständig leistungsfähiger moderner Browser ist.



application.js

Oben in dieser Datei befinden sich einige Kommentare, insbesondere der, der beginnt / * global… * /. Dies dient lediglich dazu, dem JSLint-Validator mitzuteilen, welche Variablen wir als global betrachten, damit er sie nicht als Variablen behandelt, auf die wir zugreifen möchten, ohne sie definiert zu haben. Der eigentliche Code befindet sich in Zeile 8.

// Neu definieren: $, Fenster, Dokument, undefined.var APP = (Funktion ($, Fenster, Dokument, undefiniert) {// Hinweis: Der gesamte App-Code hier, abgekürzt.// Parameter: Zepto / jQuery, Fenster, Dokument .}) (typeof Zepto === 'function'? Zepto: jQuery, this, this.document);

Falls das etwas verwirrend aussieht, lassen Sie es mich erklären. Dies wird als Abschluss bezeichnet und häufig als selbstausführende anonyme Funktion bezeichnet. Wenn wir jedoch wirklich Haare spalten, handelt es sich technisch gesehen um einen "sofort aufgerufenen Funktionsausdruck".

Grundsätzlich übergeben wir am Ende des Abschlusses Variablen nach oben. Wenn es verfügbar ist, aliase ich die Zepto-Variable auf $. Wenn es nicht vorhanden ist, weiß ich, dass ich stattdessen jQuery (für IE9) eingefügt haben muss. Ich übergebe dies als Parameter - der sich (im höchsten Bereich) auf das Fenster bezieht.Ich gehe auch vorbei dieses Dokument. Zuletzt übergebe ich undefiniert als das Konzept des Browsers des Nichts. Ich tue dies, um mich vor möglichen Spielereien zu schützen, nur als allgemeine Best Practice. Grund dafür, jemand könnten mach das…



var window = false; var document = "pwnd"; var undefined = true;

Warum diese Aspekte von JavaScript und DOM überhaupt veränderlich sind, werde ich nie verstehen. Wir haben uns jetzt versichert, dass entscheidende globale Variablen so sind, wie sie sagen, dass sie sind. Wir haben auch einen sicheren Ort für andere Variablen bereitgestellt, die wir ohne unsere äußerste Funktion definieren werden, um zu verhindern, dass auf den Code anderer Personen zugegriffen wird. Dies alles mag ein bisschen übertrieben erscheinen, aber vertrauen Sie mir - diese Gewohnheiten werden aus gutem Grund als Best Practices in JavaScript angesehen.

Der erste Codeausschnitt in unserer Schließung ist…

// Feuer ab, sobald das DOM fertig ist. $ (Document) .ready (function () {APP.go ();});

Für jeden, der jemals das kleinste bisschen jQuery gemacht hat, sollte dies vertraut aussehen. Wir warten, bis die Seite das HTML-Dokumentenskelett geladen hat (aber nicht so lange, bis alle Bilder geladen sind), und feuern dann sofort das ab APP.go Methode, die automatisch alle im APP.init-Objekt enthaltenen Funktionen aufruft.


Es gibt auch mehrere Variablen, die beiseite gelegt und als leer deklariert werden und später im definiert werden APP.init.assign_dom_vars Funktion (wird aufgerufen, nachdem das DOM bereit ist). Diese sind für alle Funktionen innerhalb der APP zugänglich, jedoch nicht darüber hinaus. Das lässt uns Variablen haben, die für uns als "Globale" fungieren, aber sonst niemand.

Sie werden feststellen, dass wir ein Objekt innerhalb unserer Schließung zurücksenden…

// Inhalt von APP.return verfügbar machen {// APP.go go: function () {// ...}, // APP.init init: {// ...}, // APP.util util: { // ...}};

Dies wird üblicherweise als Modulmuster bezeichnet. Ich werde hier nicht näher darauf eingehen, wie das funktioniert, aber wenn Sie neugierig sind (und andere JS-Entwurfsmuster), lesen Sie hier mehr.


Innerhalb der APP.init.assign_dom_vars Funktion gibt es mehrere HTML-bezogene Elemente, die Variablen zugewiesen sind. Es gibt zwei Schlüsselzeilen, die so ziemlich alle JS umfassen, die Sie schreiben müssen, um Lenker zu verwenden…


Hinweis: Wir verwenden das Schlüsselwort var hier nicht, da wir diese leeren Variablen weiter oben definiert haben (für einen höheren Bereich) und ihnen einfach echte Werte zuweisen, sobald das DOM bereit ist.

markup = $ (’#_ template-list-item’). html (). replace (/ s s + / g, ’’); template = Handlebars.compile (markup);

Die Markup-Variable ist das Ergebnis des Erfassens der innerHTML von Skript> Tag mit id = "_ template-list-item"und Entfernen aller unnötigen Leerzeichen. Dies führt zu einem geringen Platzbedarf, wenn die Informationen der einzelnen Dribbble-Aufnahmen in die Vorlage gerendert werden.

Wir übergeben diesen HTML-Code dann an den Lenker, der ihn für eine geschwindigkeitsoptimierte Wiederverwendung kompiliert. Sie fragen sich vielleicht, warum ich dieser ID einen Unterstrich vorangestellt habe. Dies ist zwar nicht erforderlich, stellt jedoch sicher, dass ich nicht versehentlich ein Styling über CSS hinzufüge, und dient als Erinnerung daran, dass es nur zum Abrufen über JS verwendet wird.


Der Rest der Funktionen in APP.init Behandeln Sie diskrete Funktionen, von denen einige von anderen Funktionen in der App aufgerufen werden. Indem wir ein gewisses Maß an Granularität beibehalten, können wir jedes weitere chirurgisch auslösen. Ich werde kurz auf jeden eingehen ...


Das APP.init.nav_shortcuts Die Funktion fügt Ereignis-Listener für die J / K-Tasten zum Blättern nach links / rechts hinzu - was ich zugegebenermaßen nur gewählt habe, weil dies die Tasten sind, die für die Paginierung in Google Reader verwendet werden. Darüber hinaus werden Ereignis-Listener für zugewiesen nach links wischen und wische nach rechts. Wenn der Code auf einem mobilen Gerät ausgeführt wird, wird oben auf der Seite auch der berührungsbezogene Tipp angezeigt. Wenn nicht, nehmen wir an, dass der Benutzer auf einem Gerät mit Tastatur surft, und zeigen stattdessen den Navigationstipp für die J / K-Tastatur an.

Das APP.init.watch_hash_change Die Funktion sucht einfach nach Änderungen an allem, was nach dem "#" in der Adressleiste des Browsers steht. Bei einer Änderung wird die Funktion APP.init.set_the_page aufgerufen. Dies wiederum greift nach dem Hash des Fensters (Beispiel: "# / page / 25") und analysiert ihn auf eine Zahl (z. B. 25). Anschließend wird das Dropdown-Menü select> auf die entsprechende Seite gesetzt und APP.util.change_hash aufgerufen, das prüft, ob Daten zwischengespeichert und gerendert wurden, oder eine Anforderung an die Dribbble-API zum Abrufen neuer Daten ausgelöst.



Das API.init.refresh_links Funktionen überschreiben einfach das Logo und die Fehlerlinks, so dass sie aufrufen window.location.reload (false)Dies ist die Möglichkeit, in JavaScript "Seite aktualisieren" zu sagen. Der einzelne false-Parameter teilt dem Browser mit, dass Sie nicht vom Server abrufen müssen, damit der Browser alles, was er bereits zwischengespeichert hat, erneut rendern kann. Dies dient nur dazu, dass der Benutzer eine Aktualisierung manuell erzwingen kann. Dies ist besonders hilfreich, wenn der Code in PhoneGap eingebettet wird, wo die häufig verwendete Browserschaltfläche "Aktualisieren" nicht vorhanden ist.

Das APP.init.page_picker Funktion behandelt die Interaktion mit dem Wählen Sie> Dropdown-Liste. Wenn der Benutzer eine Seite auswählt, die sich von der bereits ausgewählten Seite unterscheidet, wird die Seite ausgelöst API.util.change_hash Funktion, wodurch die entsprechende Seite geladen wird.

Schließlich behandelt die Funktion APP.init.external_links nur das Herausspringen nicht lokaler Links in einer neuen Browser-Registerkarte. Oder übergibt im Fall von PhoneGap den Link an den Browser des Benutzers Ihrer Wahl (Beispiel: Safari unter iOS oder Opera unter Android - wenn der Benutzer ihn auf die Standardeinstellung gesetzt hat). Wir tun dies, weil PhoneGap Links als intern behandelt, sodass Sie mehrere HTML-Seiten in Ihrer eingebetteten HTML5-App haben können. Auf diese Weise werden externe Links genauso behandelt wie jede andere Webseite - sie können mit Lesezeichen versehen, aktualisiert werden usw.


Das APP.util-Objekt beginnt mit einer Funktion, auf die wir bereits mehrmals verwiesen haben. APP.util.change_hash. Das lebt nicht in APP.init, weil es an sich nichts wirklich tut, sondern nur eine Dienstprogrammfunktion ist, die an mehreren Stellen von Funktionen aufgerufen wird, die ausgeführt werden, sobald das DOM bereit ist.

Zuletzt haben wir APP.util.load_from_api, das genau das tut, was es sagt - ruft Daten von der Dribbble-API ab. Beachten Sie hier, wie wir unsere Cache-Variable verwenden, die eine Verknüpfung zu darstellt window.localStorage für Browser, die dies unterstützen. Nachdem wir die API mit Ajax aufgerufen haben, zwischenspeichern wir den resultierenden kompilierten HTML-Code und bearbeiten alle nachfolgenden Anforderungen innerhalb einer Stunde ab unserer zwischengespeicherten Version, um unnötigen HTTP-Verkehr zu vermeiden.

Es gibt zwei Vorteile: Erstens spielen wir gut mit Dribbble und pingen ihre API nicht ständig an. Zweitens, und was noch wichtiger ist, unsere App reagiert schneller, weil sie Inhalte unter der "Wärmelampe" wiedergibt, anstatt sie kalt zu starten, und sie aus dem Dribbble-Kühlschrank holen muss (okay, schreckliche Metapher).

Wir nehmen auch einige kleinere Änderungen an den Daten vor, die von der Dribbble-API zurückgegeben werden. Beispielsweise geben einige Benutzer ihren Twitter-Namen als "@nathansmith" ein, während andere nur die gesamte URL ausschneiden und einfügen, z. B. "http://twitter.com/#!/nathansmith". Stattdessen müssen wir alles normalisieren, was in der zurückgegeben wird player.twitter_screen_name auf "nathansmith", da wir in der Lenkervorlage unser eigenes "@" voranstellen, das in der Benutzeroberfläche als "@nathansmith" gerendert wird. Ohne unnötigen Text auszusortieren, erhalten wir möglicherweise "@@ nathansmith" oder noch schlimmer "@http: //twitter.com/#! / Nathansmith".

Ebenfalls erwähnenswert: Da wir einen domänenübergreifenden Ajax-Aufruf tätigen, müssen wir JSONP verwenden, das nicht den Luxus hat, uns über einen "error:" - Status zu informieren. Stattdessen rufen wir über einen Timer ab und gehen davon aus, dass etwas schief gelaufen sein muss, wenn ein API-Aufruf länger als 10 Sekunden dauert. In diesem Fall erscheint der Fehler div>. Auch nach dieser Fehlermeldung wird jedoch aus irgendeinem Grund die Ajax-Anfrage angezeigt tut Wenn Sie erfolgreich ankommen, wird die Fehlermeldung ausgeblendet und der Inhalt wird gerendert. Es ist das Beste aus beiden Welten.

Fazit

Da haben Sie es also, eine JSON-API-gesteuerte Demo-App mit clientseitigem Caching in weniger als 250 Codezeilen. Was hier beeindruckt, ist nicht so sehr der Code, den wir selbst geschrieben haben, sondern die zusätzliche Verkettung von Zeichenfolgen, die wir dank Lenker nicht schreiben mussten.

Wenn Sie dies fasziniert hat, würde ich Sie ermutigen, sich auch Ember.js anzuschauen, ein vollständiges clientseitiges MVC-Framework, das von den Entwicklern von Lenkern erstellt wurde. Darüber hinaus möchten Sie möglicherweise auch einen Blick auf Backbone.js werfen, ein weiteres beliebtes clientseitiges Framework (erstellt vom Ersteller von CoffeeScript). Backbone.js verfügt zwar über einen eigenen Vorlagenansatz (der auf Underscore.js aufbaut), kann jedoch auch in Verbindung mit Lenkern verwendet werden.

Beliebt
Holz, das Sie glauben: von der Natur inspirierte iPhone-Hüllen
Weiter

Holz, das Sie glauben: von der Natur inspirierte iPhone-Hüllen

Al Kreativer möchten ie zweifello , da Ihre Acce oire Ihren Ge chmack im De ign wider piegeln. Die e neuen iPhone-Hüllen von Eden ind elegant, raffiniert und voller von der Natur in pirierte...
Chris Shiflett im Brooklyn Beta Summer Camp
Weiter

Chris Shiflett im Brooklyn Beta Summer Camp

Die Beta-Leute von Brooklyn, Chri hiflett und Cameron Koczon, haben da ummer Camp in Leben gerufen, ein Programm, da 25.000 U -Dollar für einen Anteil von ech Prozent in Ihr tartup inve tiert und...
5 neue Windows-Tools für kreative Profis
Weiter

5 neue Windows-Tools für kreative Profis

Kreative, die Window -Computer, -Tablet und -Telefone verwenden, haben ich traditionell darüber be chwert, da ie ich gegenüber ihren Mac-liebenden Kollegen wie arme Cou in ​​fühlen. Da ...