Ich habe kürzlich eine Nachbildung der Twitter-Herz-Animation unter den Highlights auf CodePen gesehen. Wenn ich etwas Zeit habe, schaue ich mir immer den Code von Demos an, die meine Aufmerksamkeit erregen, um zu sehen, ob etwas darin ist, das ich verwenden oder verbessern könnte. In diesem Fall war ich überrascht, dass die Demo ein Bild-Sprite verwendete. Später erfuhr ich, dass Twitter das so macht. Sicherlich könnte es ohne Bilder gemacht werden, oder?
Ich beschloss, das zu versuchen. Ich beschloss auch, es ohne JavaScript zu machen, da dies ein perfekter Kandidat für den Checkbox-Hack ist, der es Ihnen ermöglicht, einfache Ein/Aus-Umschaltungen über Formularelemente und clevere CSS zu realisieren.
Das Ergebnis

Jetzt sehen wir, wie ich es gemacht habe!
Das Original-Sprite betrachten

Es hat 29 Frames, eine Zahl, mit der ich kein Problem habe, bis es um Berechnungen geht. Dann sieht es für mich hässlich aus, weil es eine große Primzahl ist, die ich nicht durch kleine schöne Zahlen wie 2, 4 oder 5 teilen kann und eine ganze Zahl erhalte. Nun ja ... dafür sind Annäherungen gut. 29 liegt ziemlich nah an beiden 28, was ein Vielfaches von 4 ist, da 4 * 7 = 28, und 30, was ein Vielfaches von 5 ist (5 * 6 = 30). Wir könnten diese 29 also entweder als 28 oder als 30 betrachten, je nachdem, was uns am besten passt.
Das Nächste, was man am Sprite bemerkt, ist, dass er drei Komponenten hat
- das Herz
- die Blase hinter dem Herz
- die Partikel um das Herz herum
Das bedeutet, dass es mit nur einem Element und seinen zwei Pseudo-Elementen gemacht werden kann. Das Herz ist das Element selbst, die Blase ist das ::before Pseudo-Element und die Partikel sind das ::after Pseudo-Element.
Den Checkbox-Hack verwenden
Das ganze Herz und seine anderen Teile werden das <label> der Checkbox sein. Das Klicken auf das Label schaltet die Checkbox um und ermöglicht es uns, die beiden Zustände zu behandeln. In dieser Situation sieht unser HTML so aus: eine Checkbox und ein Label, das ein Unicode-Herz enthält.
<input id="toggle-heart" type="checkbox" />
<label for="toggle-heart">❤</label>
Lassen Sie uns die Checkbox aus dem Blickfeld verschwinden lassen.
[id='toggle-heart'] {
position: absolute;
left: -100vw;
}
Wir setzen dann einen color-Wert für das Herz, je nachdem, ob unsere Checkbox aktiviert ist oder nicht. Wir verwenden einen Farbwähler, um die tatsächlichen Werte aus dem Sprite zu extrahieren.
[for='toggle-heart'] {
color: #aab8c2;
}
[id='toggle-heart']:checked + label {
color: #e2264d;
}
Zentrieren und vergrößern
Wir setzen auch cursor: pointer auf das Label und erhöhen die font-size, da es sonst zu klein aussieht.
[for='toggle-heart'] {
font-size: 2em;
cursor: pointer;
}
Dann positionieren wir es in der Mitte des Bildschirms, damit wir es besser sehen können. Danke, Flexbox!
body {
display: flex;
justify-content: center; /* horizontal alignment */
margin: 0;
height: 100vh; /* the viewport height */
}
/* vertical alignment, needs the height of
the body to be equal to that of the
viewport if we want it in the middle */
[for='toggle-heart'] {
align-self: center;
}
Wir haben jetzt ein Herz, das grau ist, wenn die Checkbox nicht aktiviert ist, und karmesinrot, wenn sie es ist.
Animieren des Größenwachstums des Herzens
Wenn wir uns das Sprite ansehen, sehen wir, dass das Herz von Frame 2 bis Frame 6 auf 0 skaliert wird. Nach Frame 6 beginnt es zu wachsen und nimmt dann ab einem bestimmten Punkt etwas ab. Diese Art von Wachstum ist der perfekte Anwendungsfall für die easeOutBack-Timing-Funktion. Wir nehmen den Beginn des Wachstums bei 17,5% an, da dies eine schöne Zahl ist, die angesichts unserer Gesamtzahl von Frames eine ziemlich gute Annäherung darstellt. Nun müssen wir entscheiden, wie wir diese Skalierung durchführen. Wir können keine scale()-Transformation verwenden, da dies auch alle Nachfahren oder Pseudoelemente unseres Elements beeinflussen würde, und wir möchten nicht, dass diese auf 0 skaliert werden, wenn unser Herz es ist. Also verwenden wir font-size.
@keyframes heart { 0%, 17.5% { font-size: 0; } }
[id='toggle-heart']:checked + label {
will-change: font-size;
animation: heart 1s cubic-bezier(.17, .89, .32, 1.49);
}
Das Ergebnis des obigen Codes ist im folgenden Pen zu sehen.
Wenn wir die 0%- oder 100%-Keyframes nicht einschließen, werden sie automatisch mit den für dieses Element gesetzten Werten (in unserem Fall font-size: 2em) generiert, oder, wenn wir das nicht getan haben, aus den Standardwerten (was im Fall von font-size 1em wäre).
Die Blase
Nun zu den Pseudo-Elementen, die die Blase (und auch die Partikel, die wir als Nächstes behandeln) erstellen. Wir setzen position: relative auf unser Herz-Label, damit wir sie absolut positionieren können. Wir möchten sie unter dem Herz haben, also verwenden wir z-index: -1 dafür. Wir wollen sie in der Mitte, also bei 50% von top und left. Sowohl die Blase als auch die Partikel sind rund, also geben wir ihnen border-radius: 50%. Wir beginnen hier, die SCSS-Syntax zu verwenden, da wir sie am Ende verwenden werden, da wir einige Berechnungen durchführen müssen.
[for='toggle-heart'] {
position: relative;
&:before, &:after {
position: absolute;
z-index: -1;
top: 50%; left: 50%;
border-radius: 50%;
content: '';
}
}
Wenn wir uns das Sprite ansehen, sehen wir, dass die Blase maximal etwas mehr als doppelt so groß wie das Herz ist, also nehmen wir ihren Durchmesser auf 4,5rem. Wir verwenden rem-Einheiten, nicht em, da die font-size des Elements animiert wird, um die Größe des Herzens zu ändern. Wir dimensionieren und positionieren unser ::before Pseudo-Element in der Mitte. Wir geben ihm auch eine Test-Hintergrundfarbe, nur um zu sehen, ob es da ist und richtig aussieht (das entfernen wir später).
$bubble-d: 4.5rem; // bubble diameter
$bubble-r: .5 * $bubble-d; // bubble-radius
[for='toggle-heart']::before {
margin: -$bubble-r;
width: $bubble-d; height: $bubble-d;
background: gold;
}
Bisher alles gut.
Von Frame 2 bis Frame 5 wächst die Blase von nichts auf ihre volle Größe und wechselt von einem Karmesinrot zu einem Violett. Dann, bis Frame 9, wächst ein Loch in der Mitte, bis dieses Loch so groß ist wie die Blase selbst. Der wachsende Teil sieht aus wie eine Aufgabe, die das Animieren einer scale()-Transformation bewältigen kann. Das wachsende Loch können wir durch Animieren der border-width von $bubble-r (dem Blasenradius) auf 0 erreichen. Beachten Sie, dass wir auch box-sizing: border-box auf der Blase (dem ::before Pseudo-Element) setzen müssen, damit dies funktioniert.
[for='toggle-heart']:before {
box-sizing: border-box;
border: solid $bubble-r #e2264d;
transform: scale(0);
}
@keyframes bubble {
15% {
border-color: #cc8ef5;
border-width: $bubble-r;
transform: scale(1);
}
30%, 100% {
border-color: #cc8ef5;
border-width: 0;
transform: scale(1);
}
}
Wir können die Keyframes mit einem Mixin verdichten.
@mixin bubble($ext) {
border-color: #cc8ef5;
border-width: $ext;
transform: scale(1);
}
@keyframes bubble {
15% { @include bubble($bubble-r); }
30%, 100% { @include bubble(0); }
}
Wir lassen die Pseudo-Elemente auch die Herz-Animation erben, stellen beide auf eine easeOutCubic-Timing-Funktion um und ändern die animation-name für jedes einzeln.
[id='toggle-heart']:checked + label {
&::before, &::after {
animation: inherit;
animation-timing-function: cubic-bezier(.21, .61, .35, 1);
}
&::before {
will-change: transform, border-color, border-width;
animation-name: bubble;
}
&::after { animation-name: particles; }
}
Wir können uns ansehen, was der obige Code im folgenden Pen erzeugt.
Die Partikel
Wenn wir uns das Sprite ansehen, können wir sehen, dass wir sieben Gruppen von jeweils zwei runden Partikeln haben und diese Gruppen auf einem Kreis verteilt sind.

Was sich bei ihnen ändert, ist ihre opacity, ihre Position (weil der Radius des Kreises, auf dem die Gruppen liegen, zunimmt) und ihre Größe. Wir erstellen die Partikel mit mehreren Box-Schatten (einer für jeden Partikel) und animieren dann die opacity des Pseudo-Elements sowie die Offsets und den Spread dieser Box-Schatten.
Das Erste, was wir tun, ist, die Dimensionen eines Partikels zu bestimmen, dann unser ::after Pseudo-Element zu dimensionieren und zu positionieren.
$particle-d: 0.375rem;
$particle-r: 0.5 * $particle-d;
[for='toggle-heart']:after {
margin: -$particle-r;
width: $particle-d; height: $particle-d;
}
Wir verteilen die sieben Partikelgruppen auf einem Kreis. Wir haben 360° auf einem Kreis, wie die folgende Demo zeigt.
Wir teilen diese 360° in so viele Teile auf, wie wir Gruppen haben. Jeder Eckpunkt eines Polygons in der folgenden Demo würde die Position einer Gruppe markieren.
Wir gehen im Uhrzeigersinn und beginnen an der +-Achse der x-Achse (3 Uhr). Wenn wir bei der --Achse der y-Achse (12 Uhr) beginnen wollen, müssen wir 90° vom Winkel subtrahieren, der der Position jeder Gruppe entspricht.
Nun sehen wir, wie wir eine Verteilung von Gruppen auf einem Kreis codieren, dessen Radius wir anfangs so groß wie den Radius der Blase nehmen ($bubble-r), beginnend von oben (12 Uhr). Wenn wir davon ausgehen, dass wir nur einen Partikel in der Mitte jeder solchen Gruppe haben, dann sollte unser Code so aussehen:
$shadow-list: (); // init shadow list
$n-groups: 7; // number of groups
$group-base-angle: 360deg/$n-groups;
$group-distr-r: $bubble-r; // circular distribution radius for groups
@for $i from 0 to $n-groups {
// current group angle, starting fron 12 o'clock
$group-curr-angle: $i*$group-base-angle - 90deg;
// coords of the central point of current group of particles
$xg: $group-distr-r*cos($group-curr-angle);
$yg: $group-distr-r*sin($group-curr-angle);
// add to shadow list
$shadow-list: $shadow-list, $xg $yg;
}
Das Setzen von box-shadow: $shadow-list auf unser ::after Pseudo-Element ergibt das folgende Ergebnis.
Nun betrachten wir den Fall, dass wir zwei Partikel in jeder Gruppe haben.
Wir positionieren die Partikel in einer Gruppe auf einem Kreis (mit einem Radius, sagen wir, gleich dem Durchmesser unseres ::after Pseudo-Elements – $particle-d) um den Mittelpunkt dieser Gruppe.
Das Nächste, woran wir denken müssen, ist der Startwinkel. Im Fall der Gruppen selbst war der Startwinkel -90°, weil wir von oben beginnen wollten. Für die einzelnen Partikel ist der Startwinkel der Winkel, der der Gruppe entspricht (derjenige, den wir zur Berechnung ihrer Koordinaten verwenden), plus ein Offset-Winkel, der für alle Partikel um das Herz herum gleich ist. Wir nehmen diesen Winkel als 60° an, da dies gut aussieht.
Der Code zur Berechnung der Positionen aller Partikel und zum Hinzufügen eines box-shadow an jeder dieser Positionen ist unten aufgeführt.
$shadow-list: ();
$n-groups: 7;
$group-base-angle: 360deg/$n-groups;
$group-distr-r: $bubble-r;
$n-particles: 2;
$particle-base-angle: 360deg/$n-particles;
$particle-off-angle: 60deg; // offset angle from radius
@for $i from 0 to $n-groups {
$group-curr-angle: $i*$group-base-angle - 90deg;
$xg: $group-distr-r*cos($group-curr-angle);
$yg: $group-distr-r*sin($group-curr-angle);
@for $j from 0 to $n-particles {
$particle-curr-angle: $group-curr-angle +
$particle-off-angle + $j*$particle-base-angle;
// coordinates of curent particle
$xs: $xg + $particle-d*cos($particle-curr-angle);
$ys: $yg + $particle-d*sin($particle-curr-angle);
// add to shadow list
$shadow-list: $shadow-list, $xs $ys;
}
}
Dies führt zu dem, was im folgenden Pen zu sehen ist.
Regenbogenpartikel
Die Positionen sehen ziemlich gut aus, aber all diese Schatten verwenden den color-Wert, den wir für das Herz festgelegt haben. Wir können sie regenbogenfarben machen, indem wir jedem Partikel einen hsl()-Wert geben, der vom Index der Gruppe, in der er sich befindet ($i), und seinem Index innerhalb dieser Gruppe ($j) abhängt. Also ändern wir den Teil des Hinzufügens zur Schattenliste.
$shadow-list: $shadow-list, $xs $ys
hsl(($i + $j) * $group-base-angle, 100%, 75%);
Diese einfache Änderung ergibt Regenbogenpartikel.
Wir könnten sogar einen gewissen Grad an Zufälligkeit bei der Farbwahl einführen, aber ich war mit diesem Ergebnis ziemlich zufrieden.
Beim Animieren der Partikel wollen wir, dass sie von ihrer aktuellen Position, die bedeutet, dass die Gruppen auf dem Kreis mit dem Radius $bubble-r liegen, etwas nach außen wandern, sagen wir, bis die Gruppen auf einem Kreis mit dem Radius 1,25 * $bubble-r liegen. Das bedeutet, dass wir die Variable $group-distr-r ändern müssen.
Gleichzeitig wollen wir, dass sie von ihrer aktuellen vollen Größe auf Null schrumpfen. Das Schrumpfen von Box-Schatten ohne Weichzeichnung auf Null bedeutet, ihnen einen negativen Spreizradius zu geben, dessen absoluter Wert mindestens die Hälfte der kleinsten Dimension des Elements oder Pseudo-Elements beträgt, auf das sie angewendet werden. Beide Dimensionen unseres :after Pseudo-Elements sind gleich $particle-d (dem Partikel-Durchmesser), also sollte unser Spreizradius -$particle-r (dem Partikel-Radius) sein.
Zusammenfassend lässt sich sagen, dass wir im Zustand 0 einen Kreisverteilungsradius von $bubble-r und einen Spreizradius von 0 haben, während wir im Zustand 1 einen Kreisverteilungsradius von 1,25 * $bubble-r und einen Spreizradius von -$particle-r haben.
Wenn wir eine Variable $k für den Zustand verwenden, dann haben wir:
$group-distr-r: (1 + $k * 0.25) * $bubble-r;
$spread-r: -$k * $particle-r;
Dies führt uns dazu, ein Mixin zu erstellen, damit wir diese @for-Schleifen nicht zweimal schreiben müssen.
@mixin particles($k) {
$shadow-list: ();
$n-groups: 7;
$group-base-angle: 360deg / $n-groups;
$group-distr-r: (1 + $k * 0.25)*$bubble-r;
$n-particles: 2;
$particle-base-angle: 360deg / $n-particles;
$particle-off-angle: 60deg; // offset angle from radius
$spread-r: -$k * $particle-r;
@for $i from 0 to $n-groups {
$group-curr-angle: $i * $group-base-angle - 90deg;
$xg: $group-distr-r * cos($group-curr-angle);
$yg: $group-distr-r * sin($group-curr-angle);
@for $j from 0 to $n-particles {
$particle-curr-angle: $group-curr-angle +
$particle-off-angle + $j * $particle-base-angle;
$xs: $xg + $particle-d * cos($particle-curr-angle);
$ys: $yg + $particle-d * sin($particle-curr-angle);
$shadow-list: $shadow-list, $xs $ys 0 $spread-r
hsl(($i + $j) * $group-base-angle, 100%, 75%);
}
}
box-shadow: $shadow-list;
}
Betrachten wir nun noch einmal kurz das Sprite. Die Partikel erscheinen erst ab Frame 7. 7 ist ein Viertel (oder 25%) von 28, was ziemlich nah an unserer tatsächlichen Anzahl von Frames (29) liegt. Das bedeutet, unsere grundlegende Animation der Partikel würde etwa so aussehen.
@keyframes particles {
0%, 20% { opacity: 0; }
25% {
opacity: 1;
@include particles(0);
}
}
[for='toggle-heart']:after { @include particles(1); }
Dies ist in Aktion im folgenden Pen zu sehen.
Feinabstimmungen
In allen Browsern außer Edge/IE sieht es gut aus, wo die Partikel nicht wirklich zu nichts schrumpfen, sie bleiben da, winzig klein, kaum sichtbar, aber immer noch sichtbar. Eine schnelle Lösung dafür wäre, den absoluten Wert des Spreizradius ein kleines bisschen zu erhöhen.
$spread-r: -$k * 1.1 * $particle-r;
Ein weiteres Problem wäre die Tatsache, dass einige Betriebssysteme das Unicode-Herz durch ein Emoji-Zeichen ersetzen. Ich habe eine Lösung gefunden, die dies verhindern sollte, aber sie sieht hässlich aus und erwies sich als unzuverlässig, also habe ich am Ende einen filter von grayscale(1) angewendet, wenn die Checkbox nicht aktiviert ist, und ihn entfernt, wenn sie aktiviert wird.
Noch ein paar Tweaks wie das Setzen eines schönen background und einer font auf dem body und das Verhindern der Auswahl des Herzens, und wir erhalten.
Barrierefreiheit (Accessibility)
Dabei gibt es immer noch ein Problem, in diesem Fall ein Barrierefreiheitsproblem: Bei der Navigation mit der Tastatur gibt es keinen visuellen Hinweis darauf, ob der Herz-Schalter fokussiert ist oder nicht (da wir die Checkbox aus dem Blickfeld entfernt haben). Die erste Lösung, die mir einfällt, ist das Hinzufügen eines text-shadow auf das Herz, wenn die Checkbox fokussiert ist. Ein weißer scheint die beste Wahl zu sein.
[id='toggle-heart']:focus + label {
text-shadow:
0 0 3px #fff,
0 1px 1px #fff, 0 -1px 1px #fff,
1px 0 1px #fff, -1px 0 1px #fff;
}
Es schien nicht genügend Kontrast zum anfänglichen Grauzustand des Herzens zu haben, also habe ich das Grau aus dem Sprite in ein dunkleres geändert.
Update: Wie David Storey in den Kommentaren vorgeschlagen hat, habe ich dem Label auch aria-label='like' hinzugefügt.
Das Endergebnis
Zwei einfache Divs; sehr komplexes UI-Verhalten. Wow! Danke Ana für ein sehr detailliertes und komplexes Beispiel dafür, wie wir CSS verwenden können, um Dinge zu tun, die ziemlich kompliziert erscheinen.
Wirklich erstaunlich!!
Wirklich gut gemacht, zeigt nur, was man mit wenig Code erstellen kann :)
Sehr cooler Effekt Ana, danke fürs Teilen! Ich will den Artikel nicht schmälern, aber du hast tatsächlich zwei Elemente verwendet. Ich sah den Titel des Artikels und war baff, wie jemand das mit nur einem Element, ohne JS, ohne Bilder/SVG schaffen konnte, also dachte ich, es wäre gut, das zu erwähnen, da es sich ein wenig nach Clickbait anfühlte. Wenn Inputs keine ersetzten Elemente wären und ihre Pseudo-Elemente ihren Host-
:checked-Status übernehmen könnten, dann wäre es vielleicht mit einem Element möglich.Als potenzielle Optimierung dieser Technik könnten Sie das Label um den Input wickeln, was die Notwendigkeit von
forundidbei ihren jeweiligen Elementen überflüssig machen würde. Es würde ein drittes Element bedeuten, aber die möglichen ID-Namensraum-Albträume aus einem riesigen Twitter-Feed-Szenario beseitigen.Nochmal, danke fürs Schreiben, es war großartig!
Ich fand es schon immer etwas seltsam, Labels um Inputs zu wickeln, aber leider ist es eine ziemlich gängige Praxis.
Ich bin sicher, dass jeder Tweet eine eindeutige ID hätte, sodass er leicht angehängt werden könnte, wie z. B.
abc123_like. Das wäre das geringste der Probleme.Ich persönlich finde das Wickeln von Labels um Inputs semantisch seltsam und falsch – Inputs sind kein Teil von Labels.
Es ist auch vorzuziehen, Labels nicht um Inputs zu wickeln, aus Barrierefreiheitsgründen (bessere Screenreader-Unterstützung) und aus Barrierefreiheits-bezogenen Styling-Gründen (kein JS erforderlich, um das Ganze barrierefrei zu machen, wenn man die Checkbox/das Radio-Button versteckt, damit man seine eigene schön gestylte Version platzieren kann).
Und ja, jeder Tweet hat eine eindeutige ID – es ist die Nummer, in der die Tweet-URL endet, und wenn Sie mit den Entwicklertools nachsehen, können Sie diese Nummer in einer Reihe von IDs und Datenattributen finden. IDs wären also definitiv kein Problem im Fall von etwas wie Twitter.
Ahh! Das ist so clever, ich liebe es absolut. Danke, dass du deinen Prozess mit uns geteilt hast Ana, hier gibt es viel Inspiration. Tolle Arbeit!
Erstaunlich!!
Wow! Ana, du rockst! Beeindruckend!
Erstaunlich, was man heute mit SASS und CSS machen kann… danke fürs Teilen deines Meisterwissens über diese Sprachen… ich werde auch sagen, sehr beeindruckend…
Wow – das ist wirklich beeindruckend. Werde dieses Beispiel für einige SASS-Ideen verwenden. Tolle Arbeit!
Erstaunlich Ana! Vom Original nicht zu unterscheiden!
Alter, Respekt für die Verwendung von Schatten dafür. Schatten sind fantastisch. Ich habe Box-Schatten verwendet, um meinen Avatar zu generieren (ja, auch ein einzelnes Element).
Das ist wirklich cool, hast du die CSS-Fraktal-Demos gesehen, die Gregor Adams veröffentlicht? http://codepen.io/collection/tvJqF/
Oh wow, nein, das hatte ich nicht. Ich liebe den 3D-Fraktalbaum. Das DOM ist absolut perfekt für ein Fraktal wie dieses – es wird im Grunde zu einem richtigen Szenengraphen.
Wie würde man verhindern, dass iOS / Mac OS das Unicode-Herz durch ein Emoji-Zeichen ersetzt?
Gute Frage! Keine Ahnung, ich würde es wahrscheinlich nicht tun. Ich würde wahrscheinlich nur einen
grayscale()-Filter anwenden, wenn die Checkbox nicht aktiviert ist, und vielleicht auch Helligkeit und Sättigung ein wenig anpassen. Vielleicht auch mithue-rotate()für den aktivierten Zustand spielen, wenn es wirklich nötig ist.Beachten Sie, dass das Label ein aria-label-Attribut für die Screenreader-Barrierefreiheit haben sollte. Andernfalls liest der Screenreader etwas wie „Schweres schwarzes Herz, Checkbox, nicht angekreuzt“. Vermutlich sollte es in diesem Fall
aria-label="like"sein, da dies das ist, was die native App verwendet.Sie verwenden auch einen Button, aber ich glaube nicht, dass man das ohne JS machen könnte, denn selbst wenn man role="button" auf die Checkbox setzt oder einen Button verwendet, müsste man den aria-pressed-Status immer noch über JS hinzufügen.
Aktualisiert! :)
danke
Hallo Ana,
Es scheint, als hättest du tatsächlich meinen Beitrag vom Dezember gelesen und einige Ideen daraus übernommen :-) : http://medium.com/@OxyDesign/twitter-s-heart-animation-in-full-css-b1c00ca5b774
Gleiche Herausforderung, sie nur mit CSS/Html zu replizieren, aber mit ein paar Unterschieden.
Meine hat mehr HTML-Elemente als deine, aber das liegt hauptsächlich am Herz. Ich habe 4 Elemente verwendet, um eine Form zu erstellen, die dem Original sehr nahe kommt. Das war etwas knifflig, aber ich wollte keine Unicode-Lösung verwenden, da ich sicherstellen wollte, dass das Herz nicht je nach Gerät/Betriebssystem/Browser/Schriftart verändert wird (wenn ich mir dein Herz mit meinem Chrome/OSX ansehe, hat es nicht die gleiche Form wie in deinem GIF zum Beispiel. Wahrscheinlich wegen der Schriftart).
Zu den Partikeln: Wir haben das gleiche Konzept verwendet (mehrere Box-Schatten), aber ich habe sie auch animiert, weil sich in jedem Partikelpaar, wenn der Kreis verblasst, die Partikel nach außen bewegen, mit unterschiedlicher Geschwindigkeit, und ein Partikel wächst, während der andere schrumpft, und auch ihre Farben ändern sich.
Zur No-JS-Lösung: Ich mag die Idee des berühmten Checkbox-Tricks (ich habe ihn in vielen Projekten verwendet), aber in diesem Fall wirst du früher oder später etwas JS hinzufügen müssen, um das Liken/Entliken mit dem Server zu synchronisieren, also habe ich eine Zeile JS hinzugefügt, nur um die Klasse zu umschalten, um die Animation auszulösen (alles andere im JS in meinem Codepen dient zum Debuggen und als Demo) und tatsächlich verwendet Twitter ein Button-Element mit JS-Verhalten (sowohl zum Auslösen der Animation als auch zum Synchronisieren mit dem Server), keine Checkbox oder Form.
Da ich nie eine Antwort von dir erhalten habe (https://twitter.com/OxyDesign/status/674295377928024064), gehe ich davon aus, dass es dir gefallen hat!
Der Mixin für Partikel ist so cool!
Ich erinnere mich, dass ich diesen Pen letztes Jahr geforkt habe und nur die Form des Herzens und den Stil des Rings neu gestaltet habe.
(Ich habe
box-shadowverwendet, umborder-widthfür die Leistung zu ersetzen)http://codepen.io/Rplus/pen/advXwj?editors=0100
@Nicolas: Das hatte ich nicht gesehen. :( Ich habe tatsächlich eine Suche durchgeführt, bevor ich dies geschrieben habe, um zu sehen, ob es schon einmal gemacht wurde oder ob es noch einen Sinn hat, den Artikel zu schreiben, und es kam nichts heraus, nur Artikel, die die Sprite-Technik erklärten.
Das Herz variiert definitiv je nach Browser/Betriebssystem, auch wenn ich darauf geachtet habe, eine Schriftart explizit festzulegen. Es ist so anders als alles andere in IE!
Aber ich dachte, das wäre der schnellste Weg, außerdem bin ich normalerweise jemand, der alles vermeidet, was irgendeinen ästhetischen Sinn erfordert. Das ist etwas, das mir völlig fehlt, ich bin zu 100 % technisch und zu 0 % künstlerisch, ich weiß, dass ich Dinge vermasseln werde, wenn ich dorthin gehe. Also gehe ich einfach nicht dorthin. Dasselbe gilt für die Animation. Ich wollte nur etwas Schnelles und leicht Berechenbares. Ich weiß, meinen Animationen fehlt Finesse, das wurde mir schon mal gesagt, aber ich bin ziemlich glücklich damit, wie diese hier herausgekommen ist, angesichts meines Mangels an künstlerischem Sinn und der geringen Zeit, die ich dafür aufgewendet habe.
Ich stimme vollkommen zu, was die Verwendung von JS im Fall von Twitter angeht. Ich hätte nicht einmal darüber nachgedacht, kein JS zu verwenden, wenn ich es tatsächlich für Twitter gemacht hätte und nicht nur zum Spielen.
@Rplus: Das ist sehr interessant. Ich habe mit
box-shadowbegonnen, weil es mir wie eine bessere Idee vorkam, aber ich war mit der Animationsleistung nicht zufrieden, also bin ich zuborder-widthgewechselt. Mit der Animationsleistung bin ich auch in diesem Fall nicht zufrieden, aber immer noch etwas besser als bei derbox-shadow-Variante.@Rplus: Danke! Diese Funktion hat mich etwas Zeit gekostet, um sie zu schreiben und das genaue Verhalten der Partikel zu verstehen. Freut mich, dass es dir gefallen hat!
Was das Herz angeht, ich stimme zu, deins sieht besser aus und zunächst habe ich versucht, einfach ein Herz zu erstellen, und es sah besser aus als mein aktuelles. Aber dann habe ich versucht, es so nah wie möglich am echten aussehen zu lassen. Und das echte hat eine seltsame Form :-)
Schön für den Ring, er sieht aus wie meiner, aber wenn er performanter ist, dann super! (Auf meinem aktuellen Computer kann ich den Unterschied nicht erkennen, aber ich glaube dir.)
@Ana: Keine Sorge!
Ich mache diese Art von Herausforderungen, um auf dem Laufenden zu bleiben, was CSS angeht, da ich in den letzten Monaten und Jahren hauptsächlich mit Javascript arbeite und meine Gewohnheiten und Kenntnisse in diesem Bereich nicht verlieren möchte. Aber ich habe nicht nach irgendeiner Art von Ruhm gesucht (ich habe viele kleine Codepens und andere Tech-Posts auf Medium gemacht, die nicht mehr als ein paar hundert Aufrufe hatten) und ich war der Erste, der von der Anzahl der Aufrufe, die ich in den ersten Wochen hatte, überrascht war (derzeit fast 20.000 Aufrufe / 350 Likes, was für mich riesig ist). Und das hat mich auch zu meiner neuen Rolle geführt, die eine erstaunliche berufliche Chance für mich ist und unerwartet war.
Ich verstehe deinen Punkt bezüglich der künstlerischen Seite, es ist nur so, ich kann nicht schlafen gehen, wenn es nicht so nah wie möglich an dem ist, was ich will oder im Kopf habe, und ich kann Stunden damit verbringen, eine exakte Größe, eine exakte Bewegung usw. zu fixieren... (und diese hier hat tatsächlich einige meiner Nächte beansprucht). Vielleicht liegt es daran, dass ich ehemaliger Designer bin, könnte eine Erklärung sein :-)
Und... verwendest du hauptsächlich IE?!?
Ich verwende IE nicht hauptsächlich. Ich habe das Herz nur in allen Browsern getestet, die ich konnte, einschließlich IE.
Du musst nicht angeben (-: Sehr clevere Sache
Das ist ein großartiger Artikel, aber es ist etwas viel für jemanden, der gerade erst anfängt, Websites zu gestalten, selbst nach 3 Jahren in diesem Bereich habe ich einige Abschnitte nicht verstanden.
Erstaunlich!! Artikel :)
Das ist wirklich erstaunlich!
Ich habe einen Fork gemacht. Er verwendet viel vom SCSS und das Konzept des HTML, aber ohne IDs, damit Sie ihn beliebig oft wiederholen können. Das Konzept ist ziemlich gut, ich würde die Verwendung der HTML-Entität für das Herz bevorzugen, ich habe die Abmessungen angepasst, um zu verhindern, dass mein Bildschirm blinkt, wenn ich viele Herzen auf einer Seite habe. Ich habe auch die Umrandung konfigurierbar gemacht, da sie mir nicht gefiel, aber ich wollte sie nicht entfernen. Das ist alles persönliche Vorliebe und nichts falsch am Original, das meiner Vorstellung nach beträchtliche Zeit und Mühe gekostet hat.
http://codepen.io/Lewiscowles1986/pen/bZWQKY?editors=1100
Was die ID-Sache betrifft, siehe meinen Kommentar oben – Twitter hat bereits ein solides ID-System und das Nicht-Einbetten des Inputs in das Label bedeutet eine bessere Unterstützung durch assistive Technologien.
Also, warum HTML-Entitäten verwenden? Nach allem, was ich gefunden habe, sollte man heutzutage keine Probleme mehr bekommen, wenn man das Unicode-Herz verwendet. Und die Bedeutung eines Unicode-Herzens ist für jeden sofort klar, während ich bei
❤ohne Vorkenntnis oder Suche nicht sagen könnte, was es auf dem Bildschirm darstellt.Gedanken. Explodiert. :D
Toller Artikel!
Ich denke immer darüber nach, vom Backend zum Frontend-Entwickler zu wechseln, aber diese Sachen verblüffen mich – ich bin mir nicht sicher, ob ich auf die erstaunliche Kombination von Techniken kommen würde.
Ich möchte wissen, wie du den ursprünglichen Sprite bekommen hast.
Das ist einfach erstaunlich, wie man das mit nur 2 Divs und einer Menge CSS machen kann. Riesigen Respekt!
WOW, beeindruckend.
Ein brillanter Einsatz von box-shadows und komplexer UI-Technik aus 2 einfachen DOM-Elementen.
Ich fand es großartig.
Leser Christopher Hayes schrieb