譯者注:本文內容為 Clocks - CSS Animation 的譯文
譯者在編寫文章過程中寫了 一份最終效果(iOS7 風格)原始碼 供大家參考
時鐘
這是關於時間的。在本文中,我們將接受建立和設定時鐘動畫的挑戰,使用簡單的CSS動畫和JavaScript來觸發它們。
這是我們使用HTML,CSS,SVG背景和一些JavaScript建立的時鐘。我們將使用CSS動畫或過渡進行任何移動,並依靠JavaScript來設定初始時間並新增基本的CSS變換。
HTML
要開始使用,我們需要一些HTML。
<article class="clock">
<div class="hours-container">
<div class="hours"></div>
</div>
<div class="minutes-container">
<div class="minutes"></div>
</div>
<div class="seconds-container">
<div class="seconds"></div>
</div>
</article>
複製程式碼
我最初的方法是為每隻指標使用三個元素。然後我回去把每個包裝在一個容器元素中。雖然簡單的HTML可以用於基本的CSS動畫,但是當我們想要定位指標併為它們製作動畫時,我們還需要包含元素。
鐘面
我們將從一個基本的時鐘設計開始,圓臉,簡單的小時,分鐘和秒針。
.clock {
border-radius: 50%;
background: #fff url(/images/posts/clocks/ios_clock.svg) no-repeat center;
background-size: 88%;
height: 20em;
padding-bottom: 31%;
position: relative;
width: 20em;
}
.clock.simple:after {
background: #000;
border-radius: 50%;
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 5%;
height: 5%;
z-index: 10;
}
複製程式碼
你可以在這裡獲得SVG背景。我還新增了一個偽元素來向中心新增一個純黑色圓圈。然後可以根據需要將時鐘的指標放在該偽元素後面。
我們現在應該有這樣的結果。
在新增指標之前,我們需要放置容器。
.minutes-container, .hours-container, .seconds-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
複製程式碼
這會將每個容器堆疊在時鐘之上。接下來我們創造指標。
時針
每隻指標都被賦予absolute
屬性值並放置在十二點鐘位置。我們將從時針開始。
.hours {
background: #000;
height: 20%;
left: 48.75%;
position: absolute;
top: 30%;
transform-origin: 50% 100%;
width: 2.5%;
}
複製程式碼
我正在使用百分比來簡化時鐘縮放。這是一項複雜的工作,但能讓它們更容易適應檢視或縮小手機。我們還將transform-origin
屬性設定為指標的底部,以便以後可以旋轉。
分針
分針跟時針類似,但更長更細。
.minutes {
background: #000;
height: 40%;
left: 49%;
position: absolute;
top: 10%;
transform-origin: 50% 100%;
width: 2%;
}
複製程式碼
秒針
秒針再次變細,但也進一步向下設定,使其比中心延伸得更遠。為此,transform-origin
為80%。這使得20%的手伸出中心。
.seconds {
background: #000;
height: 45%;
left: 49.5%;
position: absolute;
top: 14%;
transform-origin: 50% 80%;
width: 1%;
z-index: 8;
}
複製程式碼
動畫
停止的時鐘每天只能正確兩次。讓我們新增一些動畫來讓時鐘表現得更像真實的東西。
有些時鐘會隨著每秒跳躍,通常會產生滴答聲。有些時鐘會隨著手的平穩移動而發出嗚嗚聲。我們兩個都試試。首先,我們會讓雙手平穩移動。
我們可以使用一個keyframe
來指示手在360度左右移動(暗示0%的起始位置)。
@keyframes rotate {
100% {
transform: rotateZ(360deg);
}
}
複製程式碼
如果使用animation
屬性應用於元素,則此關鍵幀會告訴元素在360度左右設定動畫。我們將在指標上使用linear
計時功能,使指標平穩移動。
.hours-container {
animation: rotate 43200s infinite linear;
}
.minutes-container {
animation: rotate 3600s infinite linear;
}
.seconds-container {
animation: rotate 60s infinite linear;
}
複製程式碼
時針設定為每12小時(43,400秒)旋轉一次。分針每小時旋轉一次,秒針每分鐘旋轉一次。
把它放在一起,我們現在有運動!
如果你的眼睛非常敏銳,你甚至可以讓分針移動。它需要一個小時才能完成一個旋轉,十二個小時的時間來完成它的迴圈。
秒針需要60秒,因此更容易注意到。
新增停頓
通過讓秒針在60個單獨的動作中全天候移動,我們可以讓指標更像普通時鐘。實現此目的的一種簡單方法是使用steps
計時功能。然後每隻指標的animation
屬性變為:
.minutes-container {
animation: rotate 3600s infinite steps(60);
}
.seconds-container {
animation: rotate 60s infinite steps(60);
}
複製程式碼
分針和秒針現在分六步走動。瀏覽器會自動計算這60個步驟中的每個步驟移動的距離。
正確的時間
一切都很好,時間看起來不錯,但準確度如何呢?使用一點JavaScript,我們可以讓時間對我們的訪問者來說是正確的。這是程式碼:
/*
* Starts any clocks using the user's local time
* From: cssanimation.rocks/clocks
*/
function initLocalClocks() {
// Get the local time using JS
var date = new Date;
var seconds = date.getSeconds();
var minutes = date.getMinutes();
var hours = date.getHours();
// Create an object with each hand and it's angle in degrees
var hands = [
{
hand: 'hours',
angle: (hours * 30) + (minutes / 2)
},
{
hand: 'minutes',
angle: (minutes * 6)
},
{
hand: 'seconds',
angle: (seconds * 6)
}
];
// Loop through each of these hands to set their angle
for (var j = 0; j < hands.length; j++) {
var elements = document.querySelectorAll('.' + hands[j].hand);
for (var k = 0; k < elements.length; k++) {
elements[k].style.webkitTransform = 'rotateZ('+ hands[j].angle +'deg)';
elements[k].style.transform = 'rotateZ('+ hands[j].angle +'deg)';
// If this is a minute hand, note the seconds position (to calculate minute position later)
if (hands[j].hand === 'minutes') {
elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle);
}
}
}
}
複製程式碼
此功能將時間(小時,分鐘和秒)轉換為每隻指標的相應角度。然後迴圈遍歷每隻手並使用rotateZ
的style.transform
屬性應用該角度。
這適用於除Safari或其他需要字首的瀏覽器之外的大多數瀏覽器。為此,我們還使用了style.webkitTransform
屬性。
然後,將時鐘設定為當前系統時間。
對齊秒針和分針
當首次在螢幕上繪製時鐘時,在分針需要移動之前不到一分鐘。為了實現這一點,我們可以計算出第一分鐘結束的時間並手動輕推分針。由於我們使用JavaScript進行第一次移動,我們可以使用setInterval
每分鐘繼續旋轉6度。
在我們移動分針之前,我們需要告知我們當前的分鐘數。您可能已經注意到這些線條。
if (hands[j].hand === 'minutes') {
elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle);
}
複製程式碼
這些額外的行檢查指標是否是“分鐘”指標,如果是,則使用秒針的當前角度設定資料屬性。
使用此資料屬性集,我們可以使用此資料計算何時移動分針。
/*
* Set a timeout for the first minute hand movement (less than 1 minute), then rotate it every minute after that
*/
function setUpMinuteHands() {
// Find out how far into the minute we are
var containers = document.querySelectorAll('.minutes-container');
var secondAngle = containers[0].getAttribute("data-second-angle");
if (secondAngle > 0) {
// Set a timeout until the end of the current minute, to move the hand
var delay = (((360 - secondAngle) / 6) + 0.1) * 1000;
setTimeout(function() {
moveMinuteHands(containers);
}, delay);
}
}
/*
* Do the first minute's rotation
*/
function moveMinuteHands(containers) {
for (var i = 0; i < containers.length; i++) {
containers[i].style.webkitTransform = 'rotateZ(6deg)';
containers[i].style.transform = 'rotateZ(6deg)';
}
// Then continue with a 60 second interval
setInterval(function() {
for (var i = 0; i < containers.length; i++) {
if (containers[i].angle === undefined) {
containers[i].angle = 12;
} else {
containers[i].angle += 6;
}
containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)';
containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)';
}
}, 60000);
}
複製程式碼
新增彈跳
由於我們現在使用JavaScript來移動分針,我們應該刪除動畫屬性。我們可以用過渡替換它,而不是簡單地刪除它。這是一個為動畫新增更多角色的機會。
當JavaScript為手設定新角度時,元素上的CSS轉換將告訴瀏覽器為此新位置設定動畫。這意味著JavaScript只處理簡單的角度變化,瀏覽器可以處理它的動畫。
在我們這樣做之前,我們應該更新程式碼以使用JavaScript來移動秒針。讓我們使用此程式碼為秒針容器設定動畫,每分鐘動畫60次。
/*
* Move the second containers
*/
function moveSecondHands() {
var containers = document.querySelectorAll('.seconds-container');
setInterval(function() {
for (var i = 0; i < containers.length; i++) {
if (containers[i].angle === undefined) {
containers[i].angle = 6;
} else {
containers[i].angle += 6;
}
containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)';
containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)';
}
}, 1000);
}
複製程式碼
通過JavaScript處理秒針和分針,更新CSS以使用transitions
替換animation
屬性。
.minutes-container {
transition: transform 0.3s cubic-bezier(.4,2.08,.55,.44);
}
.seconds-container {
transition: transform 0.2s cubic-bezier(.4,2.08,.55,.44);
}
複製程式碼
這些轉換適用於transform
屬性並使用cubic-bezier
定時函式。這個計時功能讓秒針有些反彈。
iOS7 風格
我非常喜歡Apple iOS 7中使用的時鐘的簡單性。他們已經伸長了指標,但我更喜歡較短的版本。
通過設計針部樣式並新增帶有數字的背景影像,我們可以輕鬆建立此外觀。
這種設計本身就是Hans Hilfiker對瑞士鐵路時鐘的演變。通過改變一些樣式並新增新的背景影像,我們可以使我們的時鐘適應這種風格。
如果您想出其他設計,請告訴我。
使用 Moment.js
我計劃這篇文章的第一個想法之一是重建酒店鐘錶場景,三個時鐘顯示不同的時區。
調整不同時區的最簡單方法是使用令人驚歎的Moment.js 時區庫。
您可以在此Codepen上檢視程式碼。
瀏覽器相容性
現代瀏覽器可以處理所涉及的CSS過渡和動畫。 IE10 +,最近的Chrome和Firefox支援它們沒有字首,Safari需要-webkit
字首。
不要忘記在JavaScript中使用字首屬性。
(完)
本文作者 Thinker
本文如有錯誤之處,請留言,我會及時更正
覺得對您有幫助的話就點個贊或收藏吧!
歡迎轉載或分享,轉載時請註明出處