Δημιουργία ρολογιού με τις συναρτήσεις τριγωνομετρίας New CSS sin() και cos().

Δημιουργία ρολογιού με τις συναρτήσεις τριγωνομετρίας New CSS sin() και cos().

Κόμβος πηγής: 1999799

Οι συναρτήσεις τριγωνομετρίας CSS είναι εδώ! Λοιπόν, είναι εάν χρησιμοποιείτε τις πιο πρόσφατες εκδόσεις του Firefox και του Safari, δηλαδή. Έχοντας αυτό το είδος μαθηματικής ισχύος στο CSS ανοίγει ένα σωρό πιθανότητες. Σε αυτό το σεμινάριο, σκέφτηκα ότι θα βουτούσαμε τα δάχτυλα των ποδιών μας στο νερό για να πάρουμε μια αίσθηση για μερικές από τις νεότερες λειτουργίες: sin() και cos().

Υπάρχουν και άλλες συναρτήσεις τριγωνομετρίας σε εξέλιξη — συμπεριλαμβανομένων tan() — γιατί λοιπόν να εστιάσουμε μόνο στο sin() και cos()? Τυχαίνει να είναι τέλεια για την ιδέα που έχω στο μυαλό μου, που είναι να τοποθετήσω κείμενο κατά μήκος της άκρης ενός κύκλου. Αυτό έχει καλυφθεί εδώ στα CSS-Tricks όταν Ο Chris μοιράστηκε μια προσέγγιση που χρησιμοποιεί μια μίξη Sass. Αυτό έγινε πριν από έξι χρόνια, οπότε ας του δώσουμε τη θεραπεία αιμορραγίας.

Να τι έχω στο μυαλό μου. Και πάλι, αυτή τη στιγμή υποστηρίζεται μόνο σε Firefox και Safari:

Έτσι, δεν είναι ακριβώς όπως οι λέξεις που σχηματίζουν ένα κυκλικό σχήμα, αλλά τοποθετούμε χαρακτήρες κειμένου κατά μήκος του κύκλου για να σχηματίσουμε μια πρόσοψη ρολογιού. Ακολουθεί κάποια σήμανση που μπορούμε να χρησιμοποιήσουμε για να ξεκινήσουμε τα πράγματα:

<div class="clock"> <div class="clock-face"> <time datetime="12:00">12</time> <time datetime="1:00">1</time> <time datetime="2:00">2</time> <time datetime="3:00">3</time> <time datetime="4:00">4</time> <time datetime="5:00">5</time> <time datetime="6:00">6</time> <time datetime="7:00">7</time> <time datetime="8:00">8</time> <time datetime="9:00">9</time> <time datetime="10:00">10</time> <time datetime="11:00">11</time> </div>
</div>

Στη συνέχεια, εδώ είναι μερικά εξαιρετικά βασικά στυλ για το .clock-face δοχείο. Αποφάσισα να χρησιμοποιήσω το <time> ετικέτα με α datetime αποδίδουν. 

.clock { --_ow: clamp(5rem, 60vw, 40rem); --_w: 88cqi; aspect-ratio: 1; background-color: tomato; border-radius: 50%; container-type: inline; display: grid; height: var(--_ow); place-content: center; position: relative; width var(--_ow);
}

Διακοσμούσα τα πράγματα λίγο εκεί, αλλά μόνο για να αποκτήσω το βασικό σχήμα και το χρώμα του φόντου που θα μας βοηθήσουν να δούμε τι κάνουμε. Παρατηρήστε πώς αποθηκεύουμε το width αξία σε α Μεταβλητή CSS. Θα το χρησιμοποιήσουμε αργότερα. Δεν πρέπει να δούμε πολλά μέχρι στιγμής:

Μεγάλος κύκλος στο χρώμα της ντομάτας με μια κατακόρυφη λίστα με τους αριθμούς 1-12 στα αριστερά.

Μοιάζει με κάποιο είδος πειράματος μοντέρνας τέχνης, σωστά; Ας εισάγουμε μια νέα μεταβλητή, --_r, για αποθήκευση του κύκλου ακτίνα κύκλου, που είναι ίσο με το μισό του πλάτους του κύκλου. Με αυτόν τον τρόπο, εάν το πλάτος (--_w) αλλάζει, η τιμή της ακτίνας (--_r) θα ενημερωθεί επίσης — χάρη σε μια άλλη μαθηματική συνάρτηση CSS, calc():

.clock { --_w: 300px; --_r: calc(var(--_w) / 2); /* rest of styles */
}

Τώρα, λίγο μαθηματικά. Ένας κύκλος είναι 360 μοίρες. Έχουμε 12 ετικέτες στο ρολόι μας, οπότε θέλουμε να τοποθετούμε τους αριθμούς κάθε 30 μοίρες (360 / 12). Στη χώρα των μαθηματικών, ένας κύκλος αρχίζει στις 3 η ώρα, άρα είναι στην πραγματικότητα το μεσημέρι μείον 90 μοίρες από αυτό, που είναι 270 μοίρες (360 - 90).

Ας προσθέσουμε μια άλλη μεταβλητή, --_d, που μπορούμε να χρησιμοποιήσουμε για να ορίσουμε ένα βαθμός τιμή για κάθε αριθμό στην πρόσοψη του ρολογιού. Θα αυξήσουμε τις τιμές κατά 30 μοίρες για να ολοκληρώσουμε τον κύκλο μας:

.clock time:nth-child(1) { --_d: 270deg; }
.clock time:nth-child(2) { --_d: 300deg; }
.clock time:nth-child(3) { --_d: 330deg; }
.clock time:nth-child(4) { --_d: 0deg; }
.clock time:nth-child(5) { --_d: 30deg; }
.clock time:nth-child(6) { --_d: 60deg; }
.clock time:nth-child(7) { --_d: 90deg; }
.clock time:nth-child(8) { --_d: 120deg; }
.clock time:nth-child(9) { --_d: 150deg; }
.clock time:nth-child(10) { --_d: 180deg; }
.clock time:nth-child(11) { --_d: 210deg; }
.clock time:nth-child(12) { --_d: 240deg; }

Εντάξει, τώρα είναι η ώρα να λερώσουμε τα χέρια μας με το sin() και cos() λειτουργίες! Αυτό που θέλουμε να κάνουμε είναι να τις χρησιμοποιήσουμε για να πάρουμε τις συντεταγμένες X και Y για κάθε αριθμό, ώστε να μπορούμε να τις τοποθετήσουμε σωστά γύρω από το ρολόι.

Ο τύπος για τη συντεταγμένη Χ είναι radius + (radius * cos(degree)). Ας το συνδέσουμε στο νέο μας --_x μεταβλητή:

--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));

Ο τύπος για τη συντεταγμένη Υ είναι radius + (radius * sin(degree)). Έχουμε αυτό που χρειαζόμαστε για να υπολογίσουμε ότι:

--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));

Υπάρχουν μερικά πράγματα καθαριότητας που πρέπει να κάνουμε για να ρυθμίσουμε τους αριθμούς, οπότε ας βάλουμε κάποιο βασικό στυλ σε αυτούς για να βεβαιωθούμε ότι είναι απολύτως τοποθετημένα και τοποθετημένα με τις συντεταγμένες μας:

.clock-face time { --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d)))); --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d)))); --_sz: 12cqi; display: grid; height: var(--_sz); left: var(--_x); place-content: center; position: absolute; top: var(--_y); width: var(--_sz);
}

Ειδοποίηση --_sz, το οποίο θα χρησιμοποιήσουμε για το width και height των αριθμών σε μια στιγμή. Ας δούμε τι έχουμε μέχρι τώρα.

Μεγάλος κύκλος σε χρώμα ντομάτας με ετικέτες ωρών εκτός κέντρου κατά μήκος της άκρης του.

Αυτό σίγουρα μοιάζει περισσότερο με ρολόι! Δείτε πώς είναι τοποθετημένη η επάνω αριστερή γωνία κάθε αριθμού στη σωστή θέση γύρω από τον κύκλο; Πρέπει να «μικρύνουμε» την ακτίνα κατά τον υπολογισμό των θέσεων για κάθε αριθμό. Μπορούμε αφαιρώ το μέγεθος ενός αριθμού (--_sz) από το μέγεθος του κύκλου (--_w), πριν υπολογίσουμε την ακτίνα:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Μεγάλος κύκλος σε χρώμα ντομάτας με ετικέτες αριθμού ωρών κατά μήκος της στρογγυλεμένης άκρης του.

Πολύ καλύτερα! Ας αλλάξουμε τα χρώματα, ώστε να φαίνεται πιο κομψό:

Λευκό ρολόι με αριθμούς σε σκούρο γκρι φόντο. Το ρολόι δεν έχει μπράτσα.

Θα μπορούσαμε να σταματήσουμε εδώ! Πετύχαμε τον στόχο της τοποθέτησης κειμένου γύρω από έναν κύκλο, σωστά; Τι είναι όμως ένα ρολόι χωρίς βραχίονες για να δείχνει ώρες, λεπτά και δευτερόλεπτα;

Ας χρησιμοποιήσουμε ένα μόνο κινούμενο σχέδιο CSS για αυτό. Αρχικά, ας προσθέσουμε τρία ακόμη στοιχεία στη σήμανση,

<div class="clock"> <!-- after <time>-tags --> <span class="arm seconds"></span> <span class="arm minutes"></span> <span class="arm hours"></span> <span class="arm center"></span>
</div>

Στη συνέχεια, κάποια κοινή σήμανση και για τους τρεις βραχίονες. Και πάλι, τα περισσότερα από αυτά είναι απλώς βεβαιωθείτε ότι οι βραχίονες είναι απολύτως τοποθετημένοι και τοποθετημένοι ανάλογα:

.arm { background-color: var(--_abg); border-radius: calc(var(--_aw) * 2); display: block; height: var(--_ah); left: calc((var(--_w) - var(--_aw)) / 2); position: absolute; top: calc((var(--_w) / 2) - var(--_ah)); transform: rotate(0deg); transform-origin: bottom; width: var(--_aw);
}

Θα χρησιμοποιήσουμε το ίδια κινούμενη εικόνα και για τους τρεις βραχίονες:

@keyframes turn { to { transform: rotate(1turn); }
}

Η μόνη διαφορά είναι ο χρόνος που χρειάζονται τα μεμονωμένα χέρια για να κάνουν μια πλήρη στροφή. Για το ώρες βραχίονα, παίρνει 12 ώρες να κάνει μια πλήρη στροφή. ο animation-duration Η ιδιότητα δέχεται τιμές μόνο σε χιλιοστά του δευτερολέπτου και δευτερολέπτων. Ας μείνουμε στα δευτερόλεπτα, που είναι 43,200 δευτερόλεπτα (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Παίρνει 1 ώρα των βραχίονα λεπτών να κάνει μια πλήρη στροφή. Αλλά θέλουμε αυτό να είναι ένα κινούμενα σχέδια πολλαπλών βημάτων έτσι η κίνηση μεταξύ των βραχιόνων είναι κλιμακωτή παρά γραμμική. Θα χρειαστούμε 60 βήματα, ένα για κάθε λεπτό:

animation: turn 3600s steps(60, end) infinite;

Η βραχίονα δευτερολέπτων is σχεδόν το ίδιο ως βραχίονας λεπτών, αλλά η διάρκεια είναι 60 δευτερόλεπτα αντί για 60 λεπτά:

animation: turn 60s steps(60, end) infinite;

Ας ενημερώσουμε τις ιδιότητες που δημιουργήσαμε στα κοινά στυλ:

.seconds { --_abg: hsl(0, 5%, 40%); --_ah: 145px; --_aw: 2px; animation: turn 60s steps(60, end) infinite;
}
.minutes { --_abg: #333; --_ah: 145px; --_aw: 6px; animation: turn 3600s steps(60, end) infinite;
}
.hours { --_abg: #333; --_ah: 110px; --_aw: 6px; animation: turn 43200s linear infinite;
}

Τι γίνεται αν θέλουμε να ξεκινήσουμε την τρέχουσα ώρα; Χρειαζόμαστε λίγο JavaScript:

const time = new Date();
const hour = -3600 * (time.getHours() % 12);
const mins = -60 * time.getMinutes();
app.style.setProperty('--_dm', `${mins}s`);
app.style.setProperty('--_dh', `${(hour+mins)}s`);

Έχω προσθέσει id="app" στην πρόσοψη του ρολογιού και ορίστε δύο νέες προσαρμοσμένες ιδιότητες σε αυτήν που ορίζουν αρνητικό animation-delay, όπως έκανε ο Mate Marschalko όταν μοιράστηκε ένα ρολόι μόνο CSS. ο getHours() μέθοδο της JavaScipt Date Το αντικείμενο χρησιμοποιεί τη μορφή 24 ωρών, επομένως χρησιμοποιούμε το remainder χειριστής για να το μετατρέψετε σε μορφή 12 ωρών.

Στο CSS, πρέπει να προσθέσουμε το animation-delay επισης:

.minutes { animation-delay: var(--_dm, 0s); /* other styles */
} .hours { animation-delay: var(--_dh, 0s); /* other styles */
}

Ένα ακόμα πράγμα. Χρησιμοποιώντας CSS @supports και τις ιδιότητες που έχουμε ήδη δημιουργήσει, μπορούμε να παρέχουμε εναλλακτική λύση σε προγράμματα περιήγησης που δεν υποστηρίζουν sin() και  cos(). (Ευχαριστώ, Temani Afif!):

@supports not (left: calc(1px * cos(45deg))) {
  time {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d)))
  }
}

Και, voilà! Το ρολόι μας τελείωσε! Ακολουθεί το τελικό demo για άλλη μια φορά. Και πάλι, αυτή τη στιγμή υποστηρίζεται μόνο σε Firefox και Safari.

Τι άλλο μπορούμε να κάνουμε;

Απλά μπερδεύουμε εδώ, αλλά μπορούμε γρήγορα να μετατρέψουμε το ρολόι μας σε μια κυκλική συλλογή εικόνων αντικαθιστώντας το <time> ετικέτες με <img> στη συνέχεια ενημέρωση του πλάτους (--_w) και ακτίνα (--_r) αξίες:

Ας δοκιμάσουμε ένα ακόμα. Ανέφερα νωρίτερα πώς το ρολόι έμοιαζε με ένα πείραμα σύγχρονης τέχνης. Μπορούμε να στηριχθούμε σε αυτό και να δημιουργήσουμε ξανά ένα μοτίβο που είδα σε μια αφίσα (που δυστυχώς δεν αγόρασα) σε μια γκαλερί τέχνης τις προάλλες. Όπως θυμάμαι, ονομαζόταν «Φεγγάρι» και αποτελούνταν από μια δέσμη κουκκίδων που σχηματίζουν έναν κύκλο.

Ένας μεγάλος κύκλος σχηματίστηκε από ένα μάτσο μικρότερους γεμάτους κύκλους διαφόρων γήινων χρωμάτων.

Αυτή τη φορά θα χρησιμοποιήσουμε μια μη ταξινομημένη λίστα, καθώς οι κύκλοι δεν ακολουθούν μια συγκεκριμένη σειρά. Δεν πρόκειται καν να βάλουμε όλα τα στοιχεία της λίστας στη σήμανση. Αντίθετα, ας τους προσθέσουμε JavaScript και ας προσθέσουμε μερικά στοιχεία ελέγχου που μπορούμε να χρησιμοποιήσουμε για να χειριστούμε το τελικό αποτέλεσμα.

Τα χειριστήρια είναι είσοδοι εύρους (<input type="range">) που θα τυλίξουμε σε ένα <form> και ακούστε το input συμβάν.

<form id="controls"> <fieldset> <label>Number of rings <input type="range" min="2" max="12" value="10" id="rings" /> </label> <label>Dots per ring <input type="range" min="5" max="12" value="7" id="dots" /> </label> <label>Spread <input type="range" min="10" max="40" value="40" id="spread" /> </label> </fieldset>
</form>

Θα εκτελέσουμε αυτήν τη μέθοδο στην "εισαγωγή", η οποία θα δημιουργήσει μια δέσμη <li> στοιχεία με το πτυχίο (--_d) μεταβλητή που χρησιμοποιήσαμε νωρίτερα και εφαρμόστηκε σε κάθε μία. Μπορούμε επίσης να επαναπροσδιορίσουμε τη μεταβλητή ακτίνας μας (--_r).

Θέλω επίσης οι κουκκίδες να είναι διαφορετικών χρωμάτων. Λοιπόν, ας τυχαιοποιήσουμε (καλά, όχι εντελώς τυχαία) την τιμή χρώματος HSL για κάθε στοιχείο λίστας και αποθηκεύστε το ως νέα μεταβλητή CSS, --_bgc:

const update = () => { let s = ""; for (let i = 1; i <= rings.valueAsNumber; i++) { const r = spread.valueAsNumber * i; const theta = coords(dots.valueAsNumber * i); for (let j = 0; j < theta.length; j++) { s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random( 50, 25 )},${random(90, 50)}%,${random(90, 60)}%)"></li>`; } } app.innerHTML = s;
}

Η random() Η μέθοδος επιλέγει μια τιμή μέσα σε ένα καθορισμένο εύρος αριθμών:

const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;

Και αυτό είναι όλο. Χρησιμοποιούμε JavaScript για την απόδοση της σήμανσης, αλλά μόλις αποδοθεί, δεν τη χρειαζόμαστε πραγματικά. ο sin() και cos() Οι λειτουργίες μας βοηθούν να τοποθετήσουμε όλες τις κουκκίδες στα σωστά σημεία.

Τελικές σκέψεις

Η τοποθέτηση πραγμάτων γύρω από έναν κύκλο είναι ένα πολύ βασικό παράδειγμα για να δείξουμε τις δυνάμεις των συναρτήσεων τριγωνομετρίας όπως sin() και cos(). Μα είναι πραγματικά Είναι ωραίο που λαμβάνουμε σύγχρονες λειτουργίες CSS που παρέχουν νέες λύσεις για παλιές λύσεις. Είμαι βέβαιος ότι θα δούμε πολύ πιο ενδιαφέρουσες, σύνθετες και δημιουργικές περιπτώσεις χρήσης, ειδικά καθώς η υποστήριξη του προγράμματος περιήγησης έρχεται στο Chrome και το Edge.

Σφραγίδα ώρας:

Περισσότερα από Κόλπα CSS