CSS だけで単位円を描くアニメーションを作る
2019-01-23最近フロントエンドを触ることが多くなってきたので、 CSS でアニメーションを描画する方法について調べてみました。
CSS でアニメーションを描画
一度だけ実行されるアニメーション
まず、クリックするごとに形が変わる要素を作ってみました。
クリックイベントに関しては JavaScript を使ってるものの、アニメーション自体は CSS のみ。以下、ソース。
まず HTML はこれだけ。
<div id="css-test-object" class="default"></div>
続いて CSS。
div#css-test-object {
width: 100px;
height: 100px;
margin: 20px 0px 20px calc(50% - 50px);
box-shadow: 0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12), 0 11px 15px -7px rgba(0,0,0,0.2);
}
div#css-test-object.default {
animation-duration: 500ms;
animation-fill-mode: forwards;
animation-name: toDefault;
}
div#css-test-object.circle {
animation-duration: 500ms;
animation-fill-mode: forwards;
animation-name: toCircle;
}
div#css-test-object.square {
animation-duration: 500ms;
animation-fill-mode: both;
animation-name: toSquare;
}
@keyframes toDefault {
from {
background-color: #01579b;
border-radius: 25%;
}
to {
background-color: #ffffff;
border-radius: 0%;
}
}
@keyframes toCircle {
from {
background-color: #ffffff;
border-radius: 0%;
}
to {
background-color: #f57f17;
border-radius: 50%;
}
}
@keyframes toSquare {
from {
background-color: #f57f17;
border-radius: 50%;
}
to {
background-color: #01579b;
border-radius: 25%;
}
}
#css-test-object
に対して 3 種類の class を準備して、それぞれのクラスがロードされた時に実行されるアニメーションを @keyframes
で定義してます。例えば .circle
の場合、
div#css-test-object.circle {
animation-duration: 500ms;
animation-fill-mode: forwards;
animation-name: toCircle;
}
@keyframes toCircle {
from {
background-color: #ffffff;
border-radius: 0%;
}
to {
background-color: #f57f17;
border-radius: 50%;
}
}
まず、 div#css-test-object.circle
に対するスタイルの指定について。
-
animation-duration
アニメーションの開始から終了までにかかる時間を指定しています。今回の場合、 500 ms つまり 0.5 秒かけて形を変化させています。 -
animation-fill-mode
アニメーションの実行前後に、指定したスタイルを適用するかを指定しています。none
,forwards
,backwards
,both
から指定します。デフォルトはnone
で、アニメーション実行時以外はそのスタイルが適用されません。forwards
だと、最後のフレーム (つまり@keyframes
のto
) で指定したスタイルが保持されます。 その他については下記参照。
animation-fill-mode | MDN -
animation-name
実行するアニメーションのキーフレームを示す@keyframes
名前を指定しています。
続いて、アニメーションの中身 toCircle
について。
-
from
開始時のスタイルを指定しています。 -
to
終了時のスタイルを指定しています。
今回の場合 toCircle
アニメーションでは、背景色が #ffffff
から #f57f17
に、要素の角丸が 0%
から 50%
に変化します。
ちなみにクリックイベントについては下記のような JavaScirpt で実装。
const mainObj = document.getElementById('css-test-object');
mainObj.addEventListener('click', (event) => {
changeObject(event);
});
function changeObject(event) {
if (event.target.classList.contains('circle')) {
event.target.classList.remove('circle');
event.target.classList.add('square');
} else if (event.target.classList.contains('square')) {
event.target.classList.remove('square');
event.target.classList.add('default');
} else {
event.target.classList.remove('default');
event.target.classList.add('circle');
}
}
繰り返し実行されるアニメーション
ドットを動かしてみる。
以下の HTML に対して、
<div class="dot-area">
<div class="dot round"></div>
</div>
以下の CSS を適用してます。
div.dot-area {
position: relative;
width: 100px;
height: 100px;
margin: 20px 0px 20px calc(50% - 50px);
}
div.dot {
position: absolute;
width: 10px;
height: 10px;
background-color: #000000;
border-radius: 50%;
left: 50px;
top: 50px;
box-shadow: 0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12), 0 11px 15px -7px rgba(0,0,class0.2);
}
div.dot.round {
animation-duration: 3000ms;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-name: round;
}
div.dot.round-smooth {
animation-duration: 3000ms;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-name: roundSmooth;
}
@keyframes round {
0% {
left: 100.0px;
top: 50.0px;
}
10.0% {
left: 65.451px;
top: 2.447px;
}
20.0% {
left: 9.549px;
top: 20.611px;
}
30.0% {
left: 9.549px;
top: 79.389px;
}
40.0% {
left: 65.451px;
top: 97.553px;
}
50.0% {
left: 100.0px;
top: 50.0px;
}
60.0% {
left: 65.451px;
top: 2.447px;
}
70.0% {
left: 9.549px;
top: 20.611px;
}
80.0% {
left: 9.549px;
top: 79.389px;
}
90.0% {
left: 65.451px;
top: 97.553px;
}
100.0% {
left: 100.0px;
top: 50.0px;
}
}
アニメーションを繰り返し実行するためには animation-iteration-count
に infinite
を指定します。ここを具体的な数値にすれば、その回数だけ繰り返されます。
keyframe について先程と違うのは、 from
と to
の代わりに、割合 (%) でスタイルを指定している点です。
アニメーションの開始時を 0%
、終了時を 100%
として、途中のスタイルを適宜指定します。
本当は単位円を描画したかったんですが、上記の記述では動きがカクカクしています。なので、より細かくスタイルを指定してみます。
@keyframes roundSmooth {
0% {
left: 100.0px;
top: 50.0px;
}
1.0% {
left: 99.606px;
top: 43.733px;
}
2.0% {
left: 98.429px;
top: 37.566px;
}
3.0% {
left: 96.489px;
top: 31.594px;
}
4.0% {
left: 93.815px;
top: 25.912px;
}
/* 略 */
98.0% {
left: 98.429px;
top: 62.434px;
}
99.0% {
left: 99.606px;
top: 56.267px;
}
100.0% {
left: 100.0px;
top: 50.0px;
}
}
こうすると、なめらかになります。
ちなみに、上のスタイルは下記のスクリプトで作ってます。三角関数知っててよかったー(棒)
import math
ACCURACY = 100
BASE_LEFT = 50
BASE_TOP = 50
RADIUS = 50
PER = 360 / ACCURACY
percent = 0
rad = 0
style = '@keyframes roundSmooth {\n'
while (percent <= 100):
left = round(BASE_LEFT + math.cos(math.radians(rad)) * RADIUS, 3)
top = round(BASE_TOP - math.sin(math.radians(rad)) * RADIUS, 3)
style += ''' {}% {{
left: {}px;
top: {}px;
}}\n'''.format(percent, left, top,)
rad += PER
percent = round((rad / 360) * RADIUS, 0)
style += '}'
keyframe_css = './keyframe.css'
with open(keyframe_css, mode='w') as f:
f.write(style)
ACCURACY
で精度、 RADIUS
で単位円の半径 (px)、 BASE_LEFT
と BASE_TOP
で基準となる位置を指定します。
以上、CSS でアニメーションを描画してみた話でした。
とりあえず基本的な部分はわかったので、より複雑なアニメーションにもチャレンジできそうです。
comments powered by Disqus