Challenge'ın ikinci gününde JS ve CSS kullanarak analog bir saat hazırlamamız gerekiyor. Projenin demosuna buradan erişebilirsiniz.
Başlangıç dosyalarını bulabileceğiniz repom: js-30 | github
1. CSS ile çubukları hareket ettirmek
Her akrep, yelkovan ve saniye çubuklarını doğru pozisyona getirmek için transform:rotate()
özelliği kullanılabilir. Ancak bu özelliği kullandığımızda varsayılan olarak çubuk tam ortasından döndürülür, bunun yerine en sonundan döndürülmesi için transform-origin
değerini 100%
yapmalıyız.
Dolayısıyla akrep, yelkovan ve saniye çubuğu tarafından kullanılan .hand
sınıfını şu şekilde güncelleyelim:
.hand {
width: 50%;
height: 6px;
background: black;
position: absolute;
top: 50%;
transform-origin: 100%;
}
2. JS ile her saniye çubukları doğru pozisyona getirmek
JS tarafında her çubuğu seçip, her saniye doğru açıda olmalarını sağlamamız gerekiyor :
<script>
const secondHand = document.querySelector(".second-hand");
const minuteHand = document.querySelector(".min-hand");
const hourHand = document.querySelector(".hour-hand");
function updateClock() {
const now = new Date();
const seconds = now.getSeconds();
// saniyenin açı değerini saklıyoruz böylece bu değeri dakikaya ekleyerek dakika çubuğunun saniye ile senkronize bir şekilde oynamasını sağlıyoruz
// örneğin dakika 30 ve saniye 20 ise dakikaya 20/60 = 0.33 dakika daha ekleyerek doğru açıda olmasını sağlayacağız.
const secondRatio = seconds / 60;
// saniyenin açısını hesaplarken +90 eklememizin sebebi varsayılan olarak çubukların saat 9 yönünde başlamalırıdır.
// +90 ekleyerek saatin 12'den başlamasını sağlıyoruz
const secondDegree = secondRatio * 360 + 90;
secondHand.style.transform = `rotate(${secondDegree}deg)`;
const minutes = now.getMinutes();
// aynı şekilde dakikanın açı değerini de saklayarak bunu saate ekleyeceğiz böylece saat çubuğu da dakika çubuğuyla senkronize olacak
const minuteRatio = (minutes + secondRatio) / 60;
const minuteDegree = minuteRatio * 360 + 90;
minuteHand.style.transform = `rotate(${minuteDegree}deg)`;
const hours = now.getHours();
const hourRatio = (hours + minuteRatio) / 12;
const hourDegree = hourRatio * 360 + 90;
hourHand.style.transform = `rotate(${hourDegree}deg)`;
}
// saatin her saniye güncellenmesi için setInterval ile updateClock fonksiyonumuzu her saniye çalıştıralım
setInterval(updateClock, 1000);
</script>
Saatin doğru bir şekilde hareket ettiğini görebilirsiniz:
3.cubic-bezier ile daha gerçekçi bir animasyon
Çubukların açısı değiştiğinde ani bir şekilde güncelleniyor. Bu geçişi gerçekci yapmak için internetten bulduğum cubic-bezier'i ekliyorum.
.hand {
width: 50%;
height: 6px;
background: black;
position: absolute;
top: 50%;
transform-origin: 100%;
transition: transform 0.3s cubic-bezier(0.4, 2.08, 0.55, 0.44);
}
Yukarıdaki gibi gerçekçi bir geçiş sağlanıyor. ANCAK saniye 0 olduğunda animasyonun bozulduğunu görüyoruz. Bunun sebebi:
- Saniye 59 iken açımız (59/ 60) * 360 + 90 = 444
- Saniye 0 olduğu anda açımız (0/60) * 360 + 90 = 90 oluyor ve eklediğimiz transition yüzünden 0.3 saniye içinde çubuğun 444deg -> 90deg arasındaki geçişini görüyoruz.
Bunu engellemek için JS tarafında şunları eklememiz yeterli olacaktır:
function updateClock() {
const now = new Date();
const seconds = now.getSeconds();
// saniyenin açı değerini saklıyoruz böylece bu değeri dakikaya ekleyerek dakika çubuğunun saniye ile senkronize bir şekilde oynamasını sağlıyoruz
// örneğin dakika 30 ve saniye 20 ise dakikaya 20/60 = 0.33 dakika daha ekleyerek doğru açıda olmasını sağlayacağız.
const secondRatio = seconds / 60;
// saniyenin açısını hesaplarken +90 eklememizin sebebi varsayılan olarak çubukların saat 9 yönünde başlamalırıdır.
// +90 ekleyerek saatin 12'den başlamasını sağlıyoruz
const secondDegree = secondRatio * 360 + 90;
// saniyenin 0 olduğunda transition kaynaklı bug için
// saniye 0 ise transition süresini 0 yapalım aksi sonrasında tekrar eski haline çevirelim
if (seconds === 0) {
secondHand.style.transitionDuration = "0s";
secondHand.style.transform = `rotate(${secondDegree}deg)`;
setTimeout(() => {
secondHand.style.transitionDuration = "0.3s";
}, 0);
} else {
secondHand.style.transform = `rotate(${secondDegree}deg)`;
}
const minutes = now.getMinutes();
// aynı şekilde dakikanın açı değerini de saklayarak bunu saate ekleyeceğiz böylece saat çubuğu da dakika çubuğuyla senkronize olacak
const minuteRatio = (minutes + secondRatio) / 60;
const minuteDegree = minuteRatio * 360 + 90;
minuteHand.style.transform = `rotate(${minuteDegree}deg)`;
const hours = now.getHours();
const hourRatio = (hours + minuteRatio) / 12;
const hourDegree = hourRatio * 360 + 90;
hourHand.style.transform = `rotate(${hourDegree}deg)`;
}
Bu projeden öğrendiklerim
transform-origin
ile CSS'de gerçekleştirilen transformların nereden başlanacağı belirlenebilir ve varsayılan olarak 50%'dir.