Yeni CSS sin() ve cos() Trigonometri Fonksiyonları ile Saat Oluşturma

Yeni CSS sin() ve cos() Trigonometri Fonksiyonları ile Saat Oluşturma

Kaynak Düğüm: 1999799

CSS trigonometri fonksiyonları burada! Eh, Firefox ve Safari'nin en son sürümlerini kullanıyorsanız, öyleler. CSS'de bu tür bir matematiksel güce sahip olmak, bir sürü olasılık açar. Bu eğitimde, birkaç yeni işlev hakkında fikir edinmek için ayak parmaklarımızı suya batıracağımızı düşündüm: sin() ve cos().

Ardışık düzende başka trigonometri fonksiyonları da var — bunlar dahil tan() - öyleyse neden sadece odaklan sin() ve cos()? Metni bir dairenin kenarı boyunca yerleştirmek olan aklımdaki fikir için mükemmeller. Bu, ne zaman CSS-Tricks'te ele alındı? Chris, Sass karışımı kullanan bir yaklaşımı paylaştı. Bu altı yıl önceydi, o yüzden hadi ona kanayan tedaviyi uygulayalım.

İşte aklımdan geçenler. Yine, şu anda yalnızca Firefox ve Safari'de desteklenmektedir:

Yani, tam olarak kelimelerin dairesel bir şekil oluşturması gibi değil, ancak bir saat yüzü oluşturmak için metin karakterlerini daire boyunca yerleştiriyoruz. İşleri başlatmak için kullanabileceğimiz bazı işaretlemeler:

<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>

Sonra, işte bazı süper temel stiller .clock-face konteyner. kullanmaya karar verdim <time> ile etiketle datetime özniteliği. 

.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);
}

Oradaki şeyleri biraz dekore ettim, ancak yalnızca ne yaptığımızı görmemize yardımcı olacak temel şekli ve arka plan rengini elde etmek için. nasıl kaydettiğimize dikkat edin width a'daki değer CSS değişkeni. Bunu daha sonra kullanacağız. Şimdiye kadar bakılacak pek bir şey yok:

Solda 1-12 arası dikey bir sayı listesi bulunan büyük domates renkli daire.

Bir çeşit modern sanat deneyi gibi görünüyor, değil mi? Yeni bir değişken tanıtalım, --_r, daireyi saklamak için yarıçap, dairenin genişliğinin yarısına eşittir. Bu şekilde genişlik (--_w) değişir, yarıçap değeri (--_r) da güncellenecektir — başka bir CSS matematik işlevi sayesinde, calc():

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

Şimdi, biraz matematik. Bir daire 360 ​​derecedir. Saatimizde 12 etiket var, bu yüzden sayıları her 30 derecede bir yerleştirmek istiyoruz (360 / 12). Matematik diyarında bir daire saat 3'te başlar, yani öğlen aslında eksi 90 derece 270 derece olan bundan (360 - 90).

Bir değişken daha ekleyelim, --_dayarlamak için kullanabileceğimiz derece saat yüzündeki her sayı için değer. Dairemizi tamamlamak için değerleri 30 derece artıracağız:

.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; }

Tamam, şimdi ellerimizi kirliyle kirletmenin zamanı geldi. sin() ve cos() fonksiyonlar! Yapmak istediğimiz şey, her sayı için X ve Y koordinatlarını elde etmek için onları kullanmak, böylece onları saat yüzünün etrafına düzgün bir şekilde yerleştirebiliriz.

X koordinatının formülü şu şekildedir: radius + (radius * cos(degree)). Hadi bunu yeni bağlantımıza takalım. --_x değişken:

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

Y koordinatının formülü şu şekildedir: radius + (radius * sin(degree)). Bunu hesaplamak için ihtiyacımız olan şeye sahibiz:

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

Numaraları ayarlamak için yapmamız gereken birkaç temizlik işi var, bu yüzden onların koordinatlarımıza göre tam olarak konumlandırıldıklarından ve yerleştirildiklerinden emin olmak için onlara bazı temel stiller koyalım:

.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);
}

ihbar --_sziçin kullanacağımız width ve height bir anda sayıların. Şimdiye kadar elimizde ne var bir bakalım.

Kenarında merkez dışı saat numarası etiketleri bulunan domates renkli büyük daire.

Bu kesinlikle daha çok bir saate benziyor! Her sayının sol üst köşesinin daire etrafında doğru yere nasıl yerleştirildiğini görüyor musunuz? Her sayı için konumları hesaplarken yarıçapı "küçültmemiz" gerekir. Yapabiliriz düşmek bir sayının boyutu (--_sz) dairenin boyutundan (--_w), yarıçapı hesaplamadan önce:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Yuvarlak kenarı boyunca saat numarası etiketleri bulunan domates renkli büyük daire.

Çok daha iyi! Daha zarif görünmesi için renkleri değiştirelim:

Koyu gri bir arka planda sayıları olan beyaz bir saat yüzü. Saatin kolları yoktur.

Tam burada durabiliriz! Metni bir dairenin etrafına yerleştirme hedefine ulaştık, değil mi? Ama kolları, dakikaları ve saniyeleri göstermeyen bir saat nedir?

Bunun için tek bir CSS animasyonu kullanalım. İlk olarak, işaretlememize üç öğe daha ekleyelim,

<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>

Ardından, üç kolun tümü için bazı ortak işaretlemeler. Yine, bunun çoğu, kolların kesinlikle buna göre konumlandırıldığından ve yerleştirildiğinden emin olmaktır:

.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);
}

Kullanacağız aynı animasyon üç kol için:

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

Tek fark, her bir kolun tam bir dönüş yapması için geçen süredir. İçin saat kolu, alır 12 saat tam bir dönüş yapmak için. bu animation-duration özelliği yalnızca milisaniye ve saniye cinsinden değerleri kabul eder. 43,200 saniye olan saniyeye bağlı kalalım (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Alır 1 saat için dakika kolu tam bir dönüş yapmak için. Ama bunun olmasını istiyoruz çok adımlı animasyon bu nedenle kollar arasındaki hareket doğrusal değil kademelidir. Her dakika için bir tane olmak üzere 60 adıma ihtiyacımız olacak:

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

The saniye kolu is hemen hemen aynı dakika kolu olarak, ancak süre 60 dakika yerine 60 saniyedir:

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

Ortak stillerde oluşturduğumuz özellikleri güncelleyelim:

.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;
}

Ya şimdiki zamanda başlamak istersek? Biraz JavaScript'e ihtiyacımız var:

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`);

ekledim id="app" saat yüzüne ve üzerinde negatif ayarlayan iki yeni özel özellik ayarlayın animation-delayMate Marschalko'nun yaptığı gibi yalnızca CSS saatini paylaştığında.  getHours() JavaScipt'in yöntemi Date nesne 24 saat biçimini kullanıyor, bu nedenle remainder Şebeke 12 saatlik biçime dönüştürmek için.

CSS'de şunu eklememiz gerekiyor: animation-delay ayrıca:

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

Sadece bir şey daha. CSS Kullanımı @supports ve zaten oluşturduğumuz özellikler, desteklemeyen tarayıcılar için bir yedek sağlayabiliriz sin() ve cos(). (Teşekkürler 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)))
  }
}

Ve işte! Saatimiz bitti! İşte bir kez daha son demo. Yine, şu anda yalnızca Firefox ve Safari'de desteklenmektedir.

Başka ne yapabiliriz?

Sadece burayı karıştırıyoruz, ancak saatimizi değiştirerek saatimizi hızla dairesel bir resim galerisine çevirebiliriz. <time> ile etiketler <img> sonra genişliği güncellemek (--_w) ve yarıçap (--_r) değerler:

Bir tane daha deneyelim. Daha önce saatin nasıl bir modern sanat deneyi gibi göründüğünden bahsetmiştim. Buna eğilebilir ve geçen gün bir sanat galerisinde bir posterde gördüğüm (maalesef satın almadığım) bir deseni yeniden yaratabiliriz. Hatırladığım kadarıyla “Ay” olarak adlandırılıyordu ve bir daire oluşturan bir grup noktadan oluşuyordu.

Çeşitli toprak tonlarından oluşan bir grup daha küçük dolu daireden oluşan büyük bir daire.

Daireler belirli bir sırayı takip etmediği için bu kez sırasız bir liste kullanacağız. Tüm liste öğelerini işaretlemeye bile koymayacağız. Bunun yerine, onlara JavaScript enjekte edelim ve nihai sonucu değiştirmek için kullanabileceğimiz birkaç kontrol ekleyelim.

Kontroller aralık girişleridir (<input type="range">) içine saracağımız <form> ve için dinle input olay.

<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>

Bu yöntemi "girdi" üzerinde çalıştıracağız, bu da bir sürü oluşturacak <li> dereceli elemanlar (--_d) daha önce kullandığımız değişken her birine uygulandı. Ayrıca yarıçap değişkenimizi (--_r).

Ayrıca noktaların farklı renklerde olmasını istiyorum. Öyleyse, rastgele seçelim (iyi, değil tamamen rastgele) her liste öğesi için HSL renk değeri ve bunu yeni bir CSS değişkeni olarak saklayın, --_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;
}

The random() yöntem, tanımlanmış bir sayı aralığında bir değer seçer:

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

Ve bu kadar. İşaretlemeyi işlemek için JavaScript kullanıyoruz, ancak işlendiği anda ona gerçekten ihtiyacımız yok. bu sin() ve cos() fonksiyonlar, tüm noktaları doğru noktalara yerleştirmemize yardımcı olur.

Nihai düşünceler

Nesneleri bir çemberin etrafına yerleştirmek, trigonometri fonksiyonlarının güçlerini göstermek için oldukça basit bir örnektir. sin() ve cos(). Ama o Gerçekten mi Eski geçici çözümler için yeni çözümler sağlayan modern CSS özelliklerine sahip olmamız harika. Özellikle Chrome ve Edge'e tarayıcı desteği geldikçe çok daha ilginç, karmaşık ve yaratıcı kullanım durumları göreceğimize eminim.

Zaman Damgası:

Den fazla CSS Püf Noktaları