CSSアニメーションだけでタイマーを作ってみた

CSSの限界に挑戦ということで、CSS3のアニメーションとCSS変数(カスタムプロパティ)を駆使して、キッチンタイマーを作ってみました。

デモ

デベロッパーツールで.timer_displayに掛かっている overflow: hidden;を解除するとアニメーションの仕組みがよく分かります。

カウントダウンタイマー(2分計)

カウントアップタイマー

仕様

  • カウントダウンとカウントアップを設定できます。カウントダウンタイマーは、計測する時間を自由に設定できます。
  • CSS変数でカウントの方向、計測時間を指定できます。
  • 真ん中の白いボタンにカーソルを当てている間は、タイマーが一時停止します。
  • カウントダウンタイマーは終了したら数字が点滅します。

解説

HTML

1
2
3
4
5
6
<div class="timer" style="--timer_min: 0;--timer_sec: 60;--timer_direction:reverse;">
<div class="timer_button"></div>
<div class="timer_display">
<div></div>
</div>
</div>

カウントの方向、計測時間を管理している変数をHTML側で指定しています。変数にとれる値は次のとおりです。

変数
–timer_min(分) 0~60
–timer_sec(秒) 0~59
–timer_direction forward(カウントアップ), reverse(カウントダウン)

カウントアップのときは、--timer_minに60、--timer_sec に00を指定してください。

CSS

当ページのデモでは、数字のフォントに別途Google FontsのShare Tech Monoを読み込んでいます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* 時計本体の装飾 */
.timer {
display: flex;
flex-direction: column;
width: 240px;
height: 280px;
margin: 20px;
padding: 30px;
background: #eb6ea0;
border-radius: 20px;
box-shadow: 2px 2px 1px 0px #ebbcce;
box-sizing: border-box;
}

/* 液晶部分の装飾 */
.timer_display {
display: flex;
justify-content: center;
width: 100%;
height: 100px;
line-height: 100px;
overflow: hidden;
background: #949494;
font-size: 60px;
border-radius: 2px;
box-shadow: 0px 0px 4px #666 inset;
font-family: 'Share Tech Mono', monospace;
text-align: center;
}

/* ボタンの装飾 */
.timer_button {
content: "";
display: block;
order: 2;
width: 40px;
height: 40px;
margin: 24px auto;
background: #fff;
border-radius: 50%;
box-shadow: -70px 0px #fff, 70px 0px #fff;
}

/* キーフレームの設定 */
@keyframes time {
0% {transform: translate(0,0px);}
100%{transform: translate(0,-6000px);}
}

@keyframes flash {
0% {opacity: 1;}
100% {opacity: 0;}
}

/* 数字を準備する */
.timer_display::before,
.timer_display::after {
display: block;
content:'00\a 01\a 02\a 03\a 04\a 05\a 06\a 07\a 08\a 09\a 10\a 11\a 12\a 13\a 14\a 15\a 16\a 17\a 18\a 19\a 20\a 21\a 22\a 23\a 24\a 25\a 26\a 27\a 28\a 29\a 30\a 31\a 32\a 33\a 34\a 35\a 36\a 37\a 38\a 39\a 40\a 41\a 42\a 43\a 44\a 45\a 46\a 47\a 48\a 49\a 50\a 51\a 52\a 53\a 54\a 55\a 56\a 57\a 58\a 59';
white-space: pre;
}

/* 分の計算・アニメーション */
.timer_display::before {
animation: time 3600s steps(60, end), flash 1s steps(2, start);
animation-direction: var(--timer_direction);
animation-delay: calc(-1s * (3600 - 60*var(--timer_min) - var(--timer_sec))), calc(1s * (60*var(--timer_min) + var(--timer_sec)));
animation-iteration-count: 1, infinite;
}

/* 秒の計算・アニメーション */
.timer_display::after {
animation: time 60s steps(60, end), flash 1s steps(2, start);
animation-direction: var(--timer_direction);
animation-delay: calc(-1s* (60 - var(--timer_sec))), calc(1s * (60*var(--timer_min) + var(--timer_sec)));
animation-iteration-count: calc(var(--timer_min) + 1), infinite;
}

/* コンマのアニメーション */
.timer_display div::before {
content: ":";
}

.timer_display div {
animation: flash 1s steps(2, start) infinite;
}

/* ボタンにhoverしたときにタイマーを止める */
.timer_button:hover + .timer_display::before,
.timer_button:hover + .timer_display::after {
animation-play-state: paused;
}
  • .timer_display::before.timer_display::afterの中に、分と秒で使う0から60の数字を改行込みで入れています。
  • CSSのanimationを用いて分と秒をバラバラに動かしています。
  • animation-delayは負の値を設定することで、再生をはじめるキーフレームの位置を指定できます。 (参考:https://teratail.com/questions/9518)今回は入力された変数を元にどこから再生を始めるかをcalcで導出しました。
  • animation-iteration-countではアニメーションの回数を指定します。分は60分で1サイクルなので1回、秒は分を元に回数を計算しています。