目錄
github:https://github.com/taozhuowei/StoreMyToys/tree/master/FlipClock
並不簡單的翻頁時鐘
我以爲的翻頁時鐘
翻頁時鐘=翻頁(transform:rotateX)+ 時鐘
實際上的翻頁時鐘
翻頁時鐘=前邊的上半部分顯示當前時間的上半部分+前邊的下半部分顯示當前時間的下半部分+後邊的上半部分顯示下一秒的時間的上半部分+後邊的下半部分顯示下一秒時間的下半部分
好的我承認,這不是句人話。所以我畫了個圖
有點簡潔,但足夠理解翻頁時鐘大概是個什麼樣子。
但要把它做出來,還需要費一番功夫。
關鍵的知識點
- html中的data-set
- css line-height=0的用處
- css flex彈性佈局
- css :after :before僞元素
- css transform
- css backface-visibility
- css animation動畫
效果展示
ScreenToGif這軟件真的挺好用...
代碼解析
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FlipClock——翻頁時鐘</title>
<link rel="stylesheet" href="ClockStyle.css">
</head>
<body>
<!--時鐘設置選項-->
<div id="option">
<h6>時間格式:</h6>
<span>24</span>
<input type="checkbox" id="controlFormat" οnclick="toggleFormat()">
<div class="checkButton"><label for="controlFormat"></label></div>
<span>12 小時制</span>
<h6>顯示秒 :</h6>
<span> 顯示</span>
<input type="checkbox" id="controlSecond" οnclick="toggleSecond()">
<div class="checkButton"><label for="controlSecond"></label></div>
<span>隱藏</span>
<!--label 的 for attribute 綁定了 id 後,點擊label會選中對應id的checkbox,
應用這個效果更改checkbox默認的樣式-->
</div>
<div id="flexContainer">
<!--時鐘整體背景-->
<div id="clockContainer">
<!--時鐘數字,時間是兩位數,每一位一個div-->
<!--data-number屬性實現簡單數據的存取
用於css和js操作其中的值-->
<!--時-->
<!--第一位(從左到右)-->
<div class="flipNumber" style="margin-left: 0">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
<!--第二位-->
<div class="flipNumber">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
<!--分隔符-->
<div class="divide">:</div>
<!--分-->
<!--第一位-->
<div class="flipNumber">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
<!--第二位-->
<div class="flipNumber">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
<!--分隔符-->
<div class="divide second">:</div>
<!--秒-->
<!--第一位-->
<div class="flipNumber second">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
<!--第二位-->
<div class="flipNumber second">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
</div>
</div>
<div id="twelve">
<p id="ampm">AM</p>
</div>
<script src="Flip.js"></script>
</body>
</html>
HTML部分不是重頭戲,唯一與以往不同的則是data-number這個attribute的出現。
那麼這個data-number是什麼呢?
它是HTML5中出現的新特性,叫做data-set自定義屬性。它可以存放一個值,便於獲取。
那在這個翻頁時鐘中它的作用就是讓css和js獲取他的值,然後把時間的數字按位置傳給他
CSS
*{margin: 0 ; padding: 0}
body{background: #191919;overflow: hidden;}
/*時鐘設置選項*/
#option{
position: absolute; top: 30px;
display: flex; justify-content: center; align-items: center;
line-height: 50px; text-align: center;
height: 50px;
font-size: 20px; color: #717171;
}
#option h6{
display: inline;
font-size: 20px;
margin-left: 20px;
}
#option span{font-size: 12px; margin-left: 10px}
/**
*更改checkbox樣式
*/
/*隱藏默認的checkbox複選框*/
#option input[type=checkbox]{visibility: hidden;}
/*滑動背景*/
.checkButton{
position: relative;
display: inline-block;
width: 50px; height: 20px;
border-radius: 15px; border: 2px solid #3b84dd;
background: #191919;
}
/*滑軌*/
.checkButton:after{
position: absolute; left: 5px; top: 9px;
z-index: 1;/*隱藏在滑塊下*/
content: '';
width:40px ; height: 1px;
background: white;
}
/*滑塊*/
#option label{
position: absolute; left: 5px;
z-index: 2;/*顯示在滑塊上*/
width: 10px; height: 10px;
margin-top: 3px;
border-radius: 100%; border: 1px solid #25f7ff;
background: #3b84dd;
box-shadow: 1px 1px 3px #25f7ff;
animation: bounceBack 1s forwards ease-in-out;
}
/*!!!!!必須使用+選擇器選擇相鄰的兄弟元素之後才能操作後邊的元素*/
#option input[type=checkbox]:checked+.checkButton label{
animation: bounce 1s forwards ease-in-out;
}
@keyframes bounce {
0%{left: 5px}
90%{left: 40px}
100%{left: 35px}
}
@keyframes bounceBack {
0%{left: 35px}
90%{left: 0}
100%{left: 5px}
}
/*彈性盒容器*/
#flexContainer{
display: flex; /*子元素的佈局方式在父元素設置*/
justify-content: center; /*水平居中*/
align-items: center;/*垂直居中*/
width: 100%; height: 100vh;
/*設置高度後align-items才生效*/
/*1vh=1%屏幕高度*/
background: #191919
}
/**
*時鐘背景
*/
#clockContainer{
display: flex; justify-content: center; align-items: center;
width: 95%; height: 350px;
border: 5px solid #5d5d5d; border-radius: 15px;
background: #242424;
}
/**
*翻頁數字容器
*/
.flipNumber{
position:relative; box-sizing: border-box;
width: 14%; height: 300px; margin-left: 1.4%;
text-align: center; font-size: 300px; line-height: 300px;
background: #ffffff;
box-shadow: 1px 1px 5px black;
}
/**
*時間分隔符
*/
.divide{
width: 2%; height: 100px; line-height: 100px;
margin-left: 1%; font-size: 6rem;
color:#717171; text-align: center;
}
/**
*頁樣式,用前後僞元素實現翻頁的樣式
*前後僞元素的值爲.time中data-number屬性的值
*before是上半頁,after是下半頁
*僞元素一個:是css2寫法,兩個::是css3寫法
*/
.time::before, .time::after{
content: attr(data-number);
position: absolute; left: 0; right: 0;
overflow: hidden;
color: #717171; background: #191919;
perspective: 100px; -webkit-perspective: 160px;
}
.time::before{
top:0; bottom: 50%;
border-bottom: 1px solid #717171;/*轉軸*/
}
.time::after{
top:50%; bottom: 0;
line-height: 0;
}
/*翻轉前*/
.flipNumber .front::after , .flipNumber .back::before{z-index: 1;}
.flipNumber .back::after{
z-index: 2;
transform-origin: center top; -webkit-transform-origin: center top;
transform: rotateX(0.5turn);/*轉半圈*/ -webkit-transform: rotateX(0.5turn);
}
.flipNumber .front::before{z-index: 3;}
/*翻轉後*/
.flipNumber.running .front::before{
transform-origin: center bottom; -webkit-transform-origin: center bottom;
animation: frontFlipDown 0.6s ease-in-out; -webkit-animation: frontFlipDown 0.6s ease-in-out;
box-shadow: 0 -2px 6px rgba(255, 255, 255, 0.3);
backface-visibility: hidden;/*隱藏背面*/ -webkit-backface-visibility: hidden;
}
.flipNumber.running .back::after{
animation: backFlipDown 0.6s ease-in-out; -webkit-animation: backFlipDown 0.6s ease-in-out;
}
/*十二小時制下提示上下午*/
#twelve{
display: none;
position: absolute; bottom: 100px; right: 40px;
width: 100px; height: 50px; line-height: 50px;
text-align: center;
border: 5px solid #5d5d5d; border-radius: 15px;
font-size:25px; color: #717171;
}
/**
*動畫
*/
@keyframes frontFlipDown {
to{transform:rotateX(0.5turn)}
}
@keyframes backFlipDown {
to{transform: rotateX(0)}
}
@-webkit-keyframes frontFlipDown {
to {-webkit-transform: rotateX(0.5turn);}
}
@-webkit-keyframes backFlipDown {
to {-webkit-transform: rotateX(0);}
}
一般的項目CSS往往只是起到修改樣式,很少着墨去詳細講它。
而翻頁時鐘不一般,所謂翻頁時鐘=翻頁+時鐘。
翻頁就是CSS , 而時鐘則是JavaScript
這篇文章我們先不討論JavaScript,重點聚焦這個CSS
詳解CSS
1.display:flex 元素居中
/*彈性盒容器*/
#flexContainer{
display: flex; /*子元素的佈局方式在父元素設置*/
justify-content: center; /*水平居中*/
align-items: center;/*垂直居中*/
width: 100%; height: 100vh;
/*設置高度後align-items才生效*/
/*1vh=1%屏幕高度*/
}
/**
*時鐘背景
*/
#clockContainer{
display: flex; justify-content: center; align-items: center;
}
/**
*翻頁數字容器
*/
.flipNumber{
}
在上面這段代碼中(無關的代碼被我刪了),flexContainer、clockContainer 和 flipNumber 三個盒子爲父子關係(爺孫?)。
flex的一個直觀的特性是,明明是規定子元素的佈局,樣式卻要寫在父元素裏面(像極了爲你好的父親......)
那麼,如何實現居中呢?只需兩步
1.justify-content :規定元素(子元素)在彈性容器(父容器)內的主線(x軸)上的對齊方式,center爲水平居中
2.align-content:規定元素在彈性容器內的垂直軸(y軸)上的對齊方式,center爲垂直居中
簡單兩步,水平垂直居中就搞定了,而垂直居中不僅可以寫在父元素內,也可以將align-self寫在子元素內規定自己的對齊方式
這裏再簡單介紹一下出現的 vh 單位 ,viewheight ,即屏幕高度,也就是鋪滿整個屏幕,很好理解。
2.:before 、:after僞元素
在css3中,這兩個僞元素被寫作 ::before 和 ::after 便於和僞類進行區分 ,一個:有助於瀏覽器兼容性
我們首先先回顧一下HTML中的結構
<!--時-->
<!--第一位(從左到右)-->
<div class="flipNumber" style="margin-left: 0">
<div class="time front" data-number="0"></div>
<div class="time back" data-number="1"></div>
</div>
這一段是小時的第一位(從左到右數)的HTML結構。
我們注意到他們的data-number分別是0和1。0是當前時間,1是接下來的時間(下一秒),在文章後續也用0代表現在,1代表下一秒。
在HTML中,0和1共有time類,而0單獨設置了front ,1單獨設置了back,很好理解,一前一後。
我們來看CSS
.time::before, .time::after{
content: attr(data-number);
position: absolute; left: 0; right: 0;
overflow: hidden;
color: #717171; background: #191919;
perspective: 100px; -webkit-perspective: 160px;
}
.time::before{
top:0; bottom: 50%;
border-bottom: 1px solid #717171;/*轉軸*/
}
.time::after{
top:50%; bottom: 0;
}
首先,創建僞類,content設置爲data-number這個attribute的值(派上用場了),絕對定位並讓其與本體完全重合。
效果似乎不太理想,而且應該在後邊的1跑到了前邊。(由於後渲染的1又是絕對定位的緣故)另外,這個1,似乎有些奇怪。
不要急,我們繼續調整。改變一下他們的層級(z-index)
/*翻轉前*/
.flipNumber .front::after , .flipNumber .back::before{z-index: 1;}
.flipNumber .back::after{
z-index: 2;
transform-origin: center top; -webkit-transform-origin: center top;
}
.flipNumber .front::before{z-index: 3;}
/*翻轉後*/
.flipNumber.running .front::before{
transform-origin: center bottom; -webkit-transform-origin: center bottom;
animation: frontFlipDown 0.6s ease-in-out; -webkit-animation: frontFlipDown 0.6s ease-in-out;
box-shadow: 0 -2px 6px rgba(255, 255, 255, 0.3);
backface-visibility: hidden;/*隱藏背面*/ -webkit-backface-visibility: hidden;
}
.flipNumber.running .back::after{
animation: backFlipDown 0.6s ease-in-out; -webkit-animation: backFlipDown 0.6s ease-in-out;
}
然後,他長這樣
這個時候,耐心已經所剩無幾,不過不要緊,幹就完了!
這個時候我們應該把1的下半部分翻上去
.flipNumber .back::after{
z-index: 2;
transform-origin: center top; -webkit-transform-origin: center top;
transform: rotateX(0.5turn);/*轉半圈*/ -webkit-transform: rotateX(0.5turn);
}
添加一句transform:rotateX(0.5turn)就好了。(0.5turn表示轉半圈)
現在層級關係對了,可是這個錯位的0並不是我們想要的。
於是,讓我們請出下一個主角。
3.line-height=0的神奇
在之前的實踐中,line-height往往用來實現文字的居中對齊。沒曾想到,他還可以等於0。並且還辣麼的好用。
讓我們加上line-height=0,看看效果
可是,爲什麼呢?
我們在這裏不從字面意義上把它理解爲行高,而是將他理解爲設置基線(元素的中心水平線)的位置。
當line-height=0時,基線的位置在盒子的頂部,那麼留在盒子裏的自然就是元素的下半部分了。
爲了方便理解上面幾點的位置關係,我又獻上了我粗糙的畫風
側視圖
好吧,可能你更懵了。不如,試試折個紙,這樣理解的會更快
我就不廢紙了,我們繼續說。
4.animation動畫
如果要我說最喜歡CSS3的哪個新特性,那答案一定是CSS的動畫。(雖然我做的還不咋地......)
但是讓時鐘翻個頁,倒也不難
/**
*動畫
*/
@keyframes frontFlipDown {
to{transform:rotateX(0.5turn)}
}
@keyframes backFlipDown {
to{transform: rotateX(0)}
}
@-webkit-keyframes frontFlipDown {
to {-webkit-transform: rotateX(0.5turn);}
}
@-webkit-keyframes backFlipDown {
to {-webkit-transform: rotateX(0);}
}
在需要動畫的元素裏寫 animation :動畫名 持續時長; 就可以爲元素簡單的綁定一個動畫,你還可以設置動畫結束後和開始前的狀態,動畫的速度等細節。
之後,用@keyframes 動畫名就可以開始創作動畫了。
最常規的寫法是包含from和to的。也可以使用百分比來控制動畫的關鍵幀。
可是問題又來了。時鐘翻頁的時候,前邊0的上半部翻下來還是可以看見,這可不好。
不怕,山人自有妙計。
5.backface-visibility:hidden
字面意思及其清晰,懂英文的一看,不用我說就能明白。
背部不可見。
一句搞定!
那麼到此爲止,樣式部分就講完了。(emmmm...關於那個按鈕的部分我會單獨出一篇文章)
歡迎關注,等待下一篇的JavaScript部分以及後續....
(網課害人,更新遙遙無期)
喜歡的話,點個關注。
如有紕漏,歡迎指正。