Schauen wir uns an, wie man die Mausposition des Benutzers ermittelt und sie in CSS Custom Properties überträgt: --positionX und --positionY.
Wir könnten das in JavaScript tun. Wenn wir das täten, könnten wir Dinge tun wie ein Element ziehbar machen oder einen Hintergrund verschieben. Aber tatsächlich können wir immer noch ähnliche Dinge tun, ohne JavaScript zu verwenden!
Ich habe diese Methode als Teil einer Demo verwendet, die ich für einen 'Ziehen und Loslassen'-Effekt mit reinem CSS erstellt habe. Ich habe die perspective-Tipps aus meinem vorherigen Artikel verwendet. Es ist ein ziemlich genialer Effekt, das alles in CSS zu erreichen, was eine breitere Anwendbarkeit haben könnte als meine Demo, also schauen wir es uns an.
Das Setup
Unsere erste Demo wird --positionX und --positionY benutzerdefinierte Eigenschaften verwenden, um die width und height eines Elements festzulegen.
Bitte beachten Sie, dass wir hier nur SCSS zur Kürze verwenden, aber all dies kann in reinem CSS erfolgen.
Dies ist unser Anfangszustand. Wir haben hier ein 'Wrapper' <div> mit einer .content-Klasse, die sich über die Breite und Höhe des Bodys erstreckt. Dieses <div> beherbergt den Inhalt unseres Projekts und die Elemente, die wir über die Mausposition steuern möchten – in diesem Fall das .square-Element.
Wir haben die beiden benutzerdefinierten Eigenschaften auch zum content hinzugefügt. Wir werden die Mausposition verwenden, um den Wert dieser Eigenschaften festzulegen, und sie dann verwenden, um die width und height des .square-Elements entsprechend festzulegen.
Sobald wir die benutzerdefinierten Eigenschaften für die Mausposition gemappt haben, können wir sie verwenden, um so ziemlich alles zu tun, was wir wollen. Zum Beispiel könnten wir sie verwenden, um die top / left-Eigenschaften eines absolut positionierten Elements festzulegen, eine transform-Eigenschaft zu steuern, die background-position festzulegen, Farben zu manipulieren oder sogar den Inhalt eines Pseudo-Elements festzulegen. Einige dieser Bonus-Demos sehen wir am Ende des Artikels.
Das Raster
Das Ziel ist es, ein unsichtbares Raster auf dem Bildschirm zu erstellen und die :hover-Pseudoklasse zu verwenden, um jede 'Zelle' mit einer Reihe von Werten zu verknüpfen, die wir den benutzerdefinierten Eigenschaften zuweisen werden. Wenn sich der Mauszeiger also auf die rechte Seite des Bildschirms bewegt, ist der Wert von --positionX höher; und wenn er sich nach links bewegt, wird er niedriger. Wir machen dasselbe mit --positionY: Der Wert wird niedriger, wenn sich der Cursor nach oben bewegt, und höher, wenn er sich nach unten bewegt.
Ein paar Worte zur Rastergröße, die wir verwenden: Wir können das Raster tatsächlich beliebig groß machen. Je größer es ist, desto genauer werden unsere benutzerdefinierten Eigenschaftswerte sein. Das bedeutet aber auch, dass wir mehr Zellen haben werden, was zu Leistungsproblemen führen kann. Es ist wichtig, ein angemessenes Gleichgewicht zu wahren und die Rastergröße an die spezifischen Bedürfnisse jedes Projekts anzupassen.
Fürs Erste sagen wir, dass wir ein 10x10-Raster für insgesamt 100 Zellen in unserem Markup wollen. (Und ja, es ist in Ordnung, Pug dafür zu verwenden, auch wenn ich es in diesem Beispiel nicht tun werde.)
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<!-- 97 more cells -->
<div class="content">
<div class="square"></div>
</div>
Sie fragen sich wahrscheinlich, warum die .cell-Elemente vor dem .content kommen. Das liegt an der Kaskade.
Wir möchten die .cell-Klasse verwenden, um das .square zu steuern, und die Art und Weise, wie die Kaskade (derzeit) funktioniert, ist, dass ein Element nur seine Kinder (oder Nachfahren) und seine Geschwister (oder deren Nachfahren) steuern kann – aber nur, solange das Geschwisterelement nach dem steuernden Element kommt.
Das bedeutet zwei Dinge:
- Jede
.cellmuss vor dem Element kommen, das wir steuern wollen (in diesem Fall das.square). - Wir können diese
.cells nicht in einen Container packen, denn wenn wir das tun, ist das.contentnicht mehr ihr Geschwister.
Positionierung der Zellen
Es gibt ein paar Möglichkeiten, die .cells zu positionieren. Wir können sie position: absolute geben und sie mit den top- und left-Eigenschaften verschieben. Oder wir können sie mit transform in Position translaten. Aber die einfachste Option ist wahrscheinlich die Verwendung von display: grid.
body {
background-color: #000;
height: 100vh;
display: grid;
grid-template: repeat(10, 1fr) / repeat(10, 1fr);
}
.cell {
width: 100%;
height: 100%;
border: 1px solid gray;
z-index: 2;
}
- Der
borderist nur temporär, damit wir diecells auf dem Bildschirm sehen können. Wir werden ihn später entfernen. - Der
z-indexist wichtig, da wir möchten, dass diecells vor demcontentliegen.
Hier ist, was wir bisher haben:
Werte hinzufügen
Wir möchten .cell verwenden, um die --positionX- und --positionY-Werte festzulegen.
Wenn wir über eine .cell fahren, die sich in der ersten (linken) Spalte befindet, sollte der Wert von --positionX 0 sein. Wenn wir über eine .cell in der zweiten Spalte fahren, sollte der Wert 1 sein. Er sollte 2 in der dritten Spalte sein und so weiter.
Dasselbe gilt für die Y-Achse. Wenn wir über eine .cell fahren, die sich in der ersten (obersten) Zeile befindet, sollte --positionY 0 sein, und wenn wir über eine cell in der zweiten Zeile fahren, sollte der Wert 1 sein und so weiter.

Die Zahlen in diesem Bild stellen die Nummern der Zellelemente im Raster dar. Wenn wir nur eine einzige .cell als Beispiel nehmen – sagen wir Zelle 42 –, können wir sie mit :nth-child() auswählen.
.cell:nth-child(42) { }
Aber wir müssen ein paar Dinge beachten:
- Wir möchten, dass dieser Selektor nur funktioniert, wenn wir über die Zelle fahren, also hängen wir
:hoverdaran. - Wir möchten das
.contentanstelle der Zelle selbst auswählen, also verwenden wir den General Sibling Combinator (~), um dies zu tun.
Um also --positionX auf 1 und --positionY auf 3 für .content festzulegen, wenn die 42. cell angefahren wird, müssen wir Folgendes tun:
.cell:nth-child(42):hover ~ .content {
--positionX: 1;
--positionY: 3;
}
Aber wer will das 100 Mal tun!? Es gibt ein paar Ansätze, um die Dinge einfacher zu machen:
- Verwenden Sie eine Sass
@for-Schleife, um alle 100 Zellen zu durchlaufen, und führen Sie einige Berechnungen durch, um die richtigen--positionX- und--positionY-Werte für jede Iteration festzulegen. - Trennen Sie die X- und Y-Achsen, um jede Zeile und jede Spalte einzeln mit der funktionalen Notation von
:nth-childauszuwählen. - Kombinieren Sie diese beiden Ansätze und verwenden Sie eine Sass
@for-Schleife *und*:nth-child-Funktionsnotation.
Ich habe lange und gründlich darüber nachgedacht, was der beste und einfachste Ansatz wäre, und obwohl alle Vor- und Nachteile haben, habe ich mich für den dritten Ansatz entschieden. Die Menge des zu schreibenden Codes, die Qualität des kompilierten Codes und die mathematische Komplexität flossen alle in mein Denken ein. Nicht einverstanden? Sagen Sie mir, warum in den Kommentaren!
Werte mit einer @for-Schleife setzen
@for $i from 0 to 10 {
.cell:nth-child(???):hover ~ .content {
--positionX: #{$i};
}
.cell:nth-child(???):hover ~ .content {
--positionY: #{$i};
}
}
Dies ist die grundlegende Schleife. Wir gehen 10 Mal durch, da wir 10 Zeilen und 10 Spalten haben. Und wir haben die X- und Y-Achsen getrennt, um --positionX für jede Spalte und --positionY für jede Zeile festzulegen. Alles, was wir jetzt tun müssen, ist, diese ??? mit der richtigen Notation zu ersetzen, um jede Zeile und Spalte auszuwählen.
Fangen wir mit der X-Achse an
Wenn wir noch einmal auf unser Rasterbild zurückblicken (das mit den Zahlen), sehen wir, dass die Zahlen aller Zellen in der zweiten Spalte Vielfache von 10 plus 2 sind. Die Zellen in der dritten Spalte sind Vielfache von 10 plus 3. Und so weiter.
Nun 'übersetzen' wir es in die funktionale Notation von :nth-child. Hier ist, wie das für die zweite Spalte aussieht:
:nth-child(10n + 2)
10nwählt jedes Vielfache von 10 aus.2ist die Spaltennummer.
Und für unsere Schleife ersetzen wir die Spaltennummer durch #{$i + 1}, um sequenziell zu iterieren.
.cell:nth-child(10n + #{$i + 1}):hover ~ .content {
--positionX: #{$i};
}
Nun kümmern wir uns um die Y-Achse
Schauen wir uns noch einmal das Rasterbild an und konzentrieren uns auf die vierte Zeile. Die Zellennummern liegen irgendwo zwischen 41 und 50. Die Zellen in der fünften Zeile liegen zwischen 51 und 60 und so weiter. Um jede Zeile auszuwählen, müssen wir ihren Bereich definieren. Zum Beispiel ist der Bereich für die vierte Zeile:
.cell:nth-child(n + 41):nth-child(-n + 50)
(n + 41)ist der Beginn des Bereichs.(-n + 50)ist das Ende des Bereichs.
Nun ersetzen wir die Zahlen durch einige Berechnungen mit dem $i-Wert. Für den Beginn des Bereichs erhalten wir (n + #{10 * $i + 1}) und für das Ende des Bereichs erhalten wir (-n + #{10 * ($i + 1)}).
Daher ist die endgültige @for-Schleife:
@for $i from 0 to 10 {
.cell:nth-child(10n + #{$i + 1}):hover ~ .content {
--positionX: #{$i};
}
.cell:nth-child(n + #{10 * $i + 1}):nth-child(-n + #{10 * ($i + 1)}):hover ~ .content {
--positionY: #{$i};
}
}
Das Mapping ist abgeschlossen! Wenn wir über Elemente fahren, ändern sich --positionX und --positionY entsprechend der Mausposition. Das bedeutet, wir können sie verwenden, um die Elemente innerhalb von .content zu steuern.
Umgang mit den benutzerdefinierten Eigenschaften
Okay, jetzt haben wir die Mausposition auf zwei benutzerdefinierte Eigenschaften abgebildet. Als Nächstes verwenden wir sie, um die width und height des .square-Elements zu steuern.
Fangen wir mit der width an und sagen, wir wollen, dass die minimale width des .square 100px beträgt (d.h. wenn sich der Mauszeiger auf der linken Seite des Bildschirms befindet), und wir wollen, dass sie sich für jeden Schritt, den sich der Mauszeiger nach rechts bewegt, um 20px vergrößert.
Mit calc() können wir das tun:
.square {
width: calc(100px + var(--positionX) * 20px);
}
Und natürlich machen wir dasselbe für die height, aber stattdessen mit --positionY:
.square {
width: calc(100px + var(--positionX) * 20px);
height: calc(100px + var(--positionY) * 20px);
}
Das war's! Jetzt haben wir ein einfaches .square-Element mit einer width und height, die von der Mausposition gesteuert wird. Bewegen Sie Ihren Mauszeiger über das Fenster und sehen Sie, wie sich die square entsprechend seiner width und height ändert.
Ich habe einen kleinen transition hinzugefügt, um den Effekt sanfter zu gestalten. Das ist natürlich nicht erforderlich. Und ich habe auch den .cell-Rand auskommentiert.
Versuchen wir eine alternative Methode
Es kann vorkommen, dass Sie --positionX und --positionY 'umgehen' und den Endwert direkt innerhalb der @for-Schleife festlegen möchten. Für unser Beispiel würde das also so aussehen:
@for $i from 0 to 10 {
.cell:nth-child(10n + #{$i + 1}):hover ~ .content {
--squareWidth: #{100 + $i * 20}px;
}
.cell:nth-child(n + #{10 * $i + 1}):nth-child(-n + #{10 * ($i + 1)}):hover ~ .content {
--squareHeight: #{100 + $i * 20}px;: #{$i};
}
}
Dann würde das .square die benutzerdefinierten Eigenschaften wie folgt verwenden:
.square {
width: var(--squareWidth);
height: var(--squareHeight);
}
Diese Methode ist etwas flexibler, da sie fortgeschrittenere Sass-Mathematik- (und Zeichenketten-) Funktionen ermöglicht. Dennoch ist das allgemeine Prinzip absolut dasselbe wie das, was wir bereits behandelt haben.
Was kommt als Nächstes?
Nun, der Rest liegt bei Ihnen – und die Möglichkeiten sind endlos! Wie würden Sie es Ihrer Meinung nach verwenden? Können Sie es weiterentwickeln? Probieren Sie diesen Trick in Ihrem CSS aus und teilen Sie Ihre Arbeit in den Kommentaren oder lassen Sie es mich auf Twitter wissen. Es wäre großartig, eine Sammlung davon zusammenkommen zu sehen.
Hier sind ein paar Beispiele, um Ihr Gehirn in Gang zu bringen:
Hallo, ich liebe den Ansatz und die Genialität Ihrer Lösung. Aber das Problem, für das ich keinen Ausweg finde, ist die Anwendung dieser Technik auf die mobile Version, wo es praktisch kein :hover gibt. Ich hoffe, Sie können diesen Abschnitt später als Kommentar oder sogar als vollständigen Artikel ausführlicher behandeln, wenn Sie Zeit haben, aber trotzdem danke für die Idee.
Zuerst einmal, danke.
Leider, wie Sie sagten, gibt es kein
:hoverbei Touch-Steuerung, daher ist diese Technik für Mobiltelefone weniger relevant. In diesem Fall können Sie JS verwenden.Es stimmt zwar, dass es auf Touchscreen-Geräten kein
:hovergibt, aber es *gibt:focus. Obwohl ich das nicht getestet habe, würde ich denken, dass Sie, wenn Sietabindex="0"zu jedem der Raster-<div>s hinzufügen,:focus(zusätzlich zu:hover) verwenden können, um es für Touchscreen-Geräte funktionsfähig zu machen. Es ist jedoch nicht klar, ob das Ziehen mit dem Finger über den Bildschirm den Fokus dem Finger folgen lässt. Berührungen würden also fast sicher funktionieren, aber das Ziehen möglicherweise nicht.Ich habe vor etwa 2 Jahren darüber geschrieben (Maus in reinem CSS verfolgen).
https://kawalekkodu.pl/follow-the-white-mouse-czyli-o-poruszaniu-obiektu-za-mysza-w-css
Sieht nach einem tollen Artikel aus.
Leider verstehe ich Polnisch nicht, daher habe ich ihn nicht gelesen, aber ich werde über dieses Thema auf einem Treffen in Warschau nächste Woche sprechen, Sie sind herzlich willkommen. :)
Nur zur Info: viele dieser Beispiele und andere ähnliche funktionieren auf Mobiltelefonen überhaupt nicht. Ich weiß, dass sie an sich schon kompliziert genug sind, aber die Einbeziehung von Hinweisen, was ohne Hover-Pseudoklassen funktioniert, würde den Lesern wirklich helfen, zu beurteilen, ob ihr Browser keine Unterstützung hat oder die Beispiele nicht.
Ich bin mir nicht sicher, wie praktikabel das für den Produktionseinsatz ist, aber es ist schon ziemlich cool!
Bezüglich Fragen zu Mobiltelefonen und Hover: es wird auf Mobiltelefonen (teilweise oder vollständig?) simuliert, indem eine Berührung gehalten wird ('gedrückt'), aber ich schätze, Sie könnten möglicherweise das Div-Raster durch Schaltflächen ersetzen...
Es ist unglaublich, was mit benutzerdefinierten Eigenschaften alles möglich ist, eine erstaunliche Technik.
Es funktioniert auf meinem Handy in einem Chromium-basierten Browser, aber vielleicht kann das Anwenden aller Dinge auf
:focussowie ein wenig mit Touch-Unterstützung helfen.Nun, ich hätte Ihren Kommentar lesen sollen, bevor ich meinen gepostet habe :-)
Tolle Demo, mir gefällt, wie Sie auf dieser Arbeit aufbauen (ich weiß nicht, ob es absichtlich ist) https://translate.google.com/translate?sl=pl&tl=en&u=https://kawalekkodu.pl/follow-the-white-mouse-czyli-o-poruszaniu-obiektu-za-mysza-w-css
Die Matrixidee war schon da, aber Sie haben eine interessante Schicht mit Variablen hinzugefügt, die sie an einigen Stellen viel wartungsfreundlicher machen kann :)
Wenn es keine Hacker, Betrüger und andere Bedrohungen gäbe, hätten wir längst reine CSS-Mauspositionen, Tastaturverfolgung und andere hardwarebezogene Symbole in CSS.
Zum Beispiel haben wir all die ausgefallenen Filter-, Farbkorrektur-, Schriftarten-, Misch-, eingebetteten Bild- und Verformungsmöglichkeiten, die man sich nur wünschen kann! Positionsvariablen würden die Lücke schließen und CSS so viel mächtiger machen!
Aber es wird niemals passieren. Dank der Ausbeuter muss Sicherheit immer an vorderster Front jedes Standards stehen. Vielen Dank von ganzem Herzen an diejenigen in der Welt, die nur die Bemühungen anderer ausnutzen. Sarkasmus natürlich.
Ich habe Schwierigkeiten zu glauben, dass die Menschheit jemals an einem Punkt angelangen wird, an dem wir nicht jede Gelegenheit ausnutzen und jeder jederzeit Zurückhaltung übt. Stellen Sie sich vor, was unsere Technologie leisten könnte, wenn es nicht all die Sicherheitsaufblähung gäbe. Es ist einfach zu schade, dass wir nicht einfach eine Sache sagen und sie alle zur Besserung bewegen können.
Also haben wir dieses Beispiel, das sich damit auseinandersetzen muss. Ich applaudiere Ihnen, aber leider dürfen Sie, wenn Sie ein Teammitglied sind, oft nicht selbst die CSS-HTML-Brücke überqueren und müssen die HTML-Entwickler bitten, Variablen für Sie hinzuzufügen, wenn Sie es wirklich können. Es ist so ärgerlich, dass wir das tun müssen. Andernfalls müssen wir andere Wege finden oder unseren Chefs erklären, warum es mit reinem CSS nicht möglich ist. Dann gehen wir nach Hause und verfluchen Ausbeuter, wie ich es gerade getan habe.