背景
業務にてHTMLとCSSをガッツリ触るタイミングがありました
久しぶりだったというのもありCSSアニメーションで遊びたいと思いました
CSSアニメーション遊んでいる中「こんなことが出来るんだ!」というものを少しでも広めらたらいいな、と思い記事を書く運びになりました
コードについて
学生時代にCSSアニメーションの神・yui540さんを知り、色々とCSSアニメーションを真似ていました
社会人になってからCSSアニメーションにあまり触っていなかったので読み辛いコードになっているかもしれません
温かい心で見ていただければ幸いです
CSSアニメーションについて
処理の種類として大まかに2つの処理があるかと思います
transition
:hover
や:active
などの擬似クラスをトリガーとしてアニメーションを行う処理です
簡単に実装出来るため様々なサイトで見ます
animation
擬似クラスではなく自分自身で処理を行うタイミングを決めてアニメーションを行えます
複雑なアニメーションを実装出来ます
本記事ではこちらを多用しています
アニメーションの探し方
CSSアニメーションでどのようなものを作るかはYouTubeで
- ゲームのOP映像
- アニメのOP映像
- 曲のMV
などから探す良いかと思います
基本的な動作
2つのボックスにそれぞれのアニメーションを動かしていきます
・1つ目のボックス: 色の変化
・2つ目のボックス: 拡大
<div class="example">
<div class="animation__1"></div>
<div class="animation__2"></div>
</div>
.example {
display: flex;
justify-content: center;
align-items: center;
gap: 50px;
}
.animation__1 {
position: relative;
width: 100px;
height: 100px;
background: #f00;
animation: animation__1 2s infinite alternate;
}
.animation__2 {
width: 100px;
height: 100px;
background: #f00;
animation: animation__2 2s infinite alternate;
transform: scale(1, 1);
transform-origin: top left;
}
@keyframes animation__1 {
0% {
background: #f00;
}
100% {
background: #0f0;
}
}
@keyframes animation__2 {
0% {
transform: scale(1, 1);
}
100% {
transform: scale(2, 2);
}
}
各プロパティの説明
animation
-
animation
:animation__1 2s infinite alternate;
-
animation-name
: 使用する@keyframes
の名前(animation__1
やanimation__2
) -
animation-duration
: 2秒かけてアニメーションを実行 -
animation-iteration-count
:infinite
で無限にループ -
animation-direction
:alternate
で順方向と逆方向を交互に繰り返す
@keyframes
@keyframes
を使用してアニメーションの変化を定義します。
背景色の変更(animation__1
)
-
0%
: 背景色を赤 (#f00
) -
100%
: 背景色を緑 (#0f0
)
拡大アニメーション(animation__2
)
-
0%
:transform: scale(1, 1);
(元のサイズ) -
100%
:transform: scale(2, 2);
(2倍のサイズに拡大)
transform
-
transform: scale(1, 1);
:元のサイズ -
transform: scale(2, 2);
:横・縦方向に2倍のサイズに拡大
transform-origin
-
transform-origin: top left;
:変形の基準点を左上に設定
画面全体を動くアニメーション
複数のレイヤーが上下左右からスライドしながら表示される仕組みになっています。
<div class="animation1">
<div>
<div></div>
<div></div>
</div>
<div></div>
<div></div>
<div></div>
</div>
$start_animation: cubic-bezier(0.29, 0.92, 0.73, 1);
$end_animation: cubic-bezier(0.8, 0, 0.92, 0.19);
.animation1 {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.animation1 > div {
position: absolute;
top: 0;
width: 100vw;
height: 100vh;
&:nth-child(1) {
left: 0;
display: flex;
& > div {
height: 100%;
position: absolute;
transform: translateY(-100%);
animation: animation__1 2s forwards;
animation-timing-function: $start_animation;
&:nth-child(1) {
z-index: 6;
left: 0;
width: 50%;
background: #5b6a84;
clip-path: polygon(100% 50%, 0% 100%, 0% 0%);
}
&:nth-child(2) {
z-index: 1;
right: 0;
width: 100%;
background: #5b6a84;
clip-path: polygon(100% 0%, 50% 50%, 0% 0%);
}
}
}
&:nth-child(2) {
z-index: 2;
right: 0;
background: #f28396;
transform: translateY(-100%);
animation: animation__2 2s forwards;
animation-timing-function: $start_animation;
clip-path: polygon(100% 0%, 100% 100%, 0% 0%);
}
&:nth-child(3) {
z-index: 3;
right: 0;
transform: translateY(100%);
background: linear-gradient(45deg, #596c85, #7086a9);
animation: animation__3 2s forwards;
animation-timing-function: $start_animation;
clip-path: polygon(0% 100%, 100% 100%, 100% 0%);
}
&:nth-child(4) {
z-index: 4;
left: 0;
background: linear-gradient(45deg, #f58c8f, #be6c6d);
transform: translateY(100%);
animation: animation__4 2s forwards;
animation-timing-function: $start_animation;
clip-path: polygon(0% 100%, 100% 100%, 0% 0%);
}
}
@keyframes animation__1 {
0% {
transform: translateY(-100%);
}
50% {
transform: translateY(0%);
animation-timing-function: $end_animation;
}
65% {
animation-timing-function: $end_animation;
transform: translateY(-100%);
}
100% {
animation-timing-function: $end_animation;
transform: translateY(-100%);
}
}
@keyframes animation__2 {
0% {
transform: translateY(-100%);
}
50% {
transform: translateY(0%);
animation-timing-function: $end_animation;
}
65% {
transform: translateY(-100%);
animation-timing-function: $end_animation;
}
100% {
transform: translateY(-100%);
animation-timing-function: $end_animation;
}
}
@keyframes animation__3 {
0% {
transform: translateY(100%);
}
50% {
transform: translateY(0%);
animation-timing-function: $end_animation;
}
65% {
transform: translateY(100%);
animation-timing-function: $end_animation;
}
100% {
transform: translateY(100%);
animation-timing-function: $end_animation;
}
}
@keyframes animation__4 {
0% {
transform: translateY(100%);
}
50% {
transform: translateY(0%);
animation-timing-function: $end_animation;
}
65% {
transform: translateY(100%);
animation-timing-function: $end_animation;
}
100% {
transform: translateY(100%);
animation-timing-function: $end_animation;
}
}
最初に設定するだけでなくアニメーションの途中で任意のイージングに変更が可能です
下記のコードで言うと最初はcubic-bezier(0.29, 0.92, 0.73, 1)
でアニメーションが行われ、50%以降はcubic-bezier(0.8, 0, 0.92, 0.19)
でアニメーションが行われます
$start_animation: cubic-bezier(0.29, 0.92, 0.73, 1);
$end_animation: cubic-bezier(0.8, 0, 0.92, 0.19);
&:nth-child(1) {
left: 0;
display: flex;
& > div {
height: 100%;
position: absolute;
transform: translateY(-100%);
animation: animation__1 2s forwards;
animation-timing-function: $start_animation;
&:nth-child(1) {
z-index: 6;
left: 0;
width: 50%;
background: #5b6a84;
clip-path: polygon(100% 50%, 0% 100%, 0% 0%);
}
&:nth-child(2) {
z-index: 1;
right: 0;
width: 100%;
background: #5b6a84;
clip-path: polygon(100% 0%, 50% 50%, 0% 0%);
}
}
}
@keyframes animation__1 {
0% {
transform: translateY(-100%);
}
50% {
transform: translateY(0%);
animation-timing-function: $end_animation;
}
65% {
animation-timing-function: $end_animation;
transform: translateY(-100%);
}
100% {
animation-timing-function: $end_animation;
transform: translateY(-100%);
}
}
テキストへのアニメーション
テキストに対してアニメーションを行っていきます
予想ですがSVGで行ったほうが良いかも、と思っていますがまだSVGのアニメーションに慣れていないので使っておりません
<div class="animation2">
<div class="text__wrap">
<div class="text">テ</div>
<div class="text">テ</div>
<div class="text">キ</div>
<div class="text">キ</div>
<div class="text">ス</div>
<div class="text">ス</div>
<div class="text">ト</div>
<div class="text">ト</div>
</div>
</div>
@mixin leftText($inDelay, $outDelay) {
transform: translateY(-100%);
-webkit-mask-image: linear-gradient(to right, black 50%, transparent 50%);
mask-image: linear-gradient(to right, black 50%, transparent 50%);
animation: inTop 0.2s ease-out $inDelay forwards,
outTop 0.2s ease-in $outDelay forwards;
}
@mixin rightText($inDelay, $outDelay) {
transform: translateY(100%);
-webkit-mask-image: linear-gradient(to left, black 50%, transparent 50%);
mask-image: linear-gradient(to left, black 50%, transparent 50%);
animation: inDown 0.2s ease-out $inDelay forwards,
outDown 0.2s ease-in $outDelay forwards;
}
@mixin rotate() {
animation: rotate1 0.2s ease-in-out 0.2s forwards,
rotate2 0.2s ease-in-out 0.6s forwards,
rotate3 0.2s ease-in-out 1.1s forwards,
rotate4 0.2s ease-in-out 1.6s forwards,
rotate5 0.2s ease-in-out 2.1s forwards;
}
.animation3 {
.text__wrap {
position: relative;
width: 10rem;
height: 10rem;
border: solid 0.5rem #aaa;
background: #242424;
overflow: hidden;
z-index: 1;
& > .text {
top: 0;
left: 0;
position: absolute;
font-size: 10rem;
letter-spacing: 0;
line-height: 1;
font-weight: bold;
&:nth-child(1) {
@include leftText(0.2s, 0.6s);
}
&:nth-child(2) {
@include rightText(0.2s, 0.6s);
}
&:nth-child(3) {
@include leftText(0.7s, 1.1s);
}
&:nth-child(4) {
@include rightText(0.7s, 1.1s);
}
&:nth-child(5) {
@include leftText(1.2s, 1.6s);
}
&:nth-child(6) {
@include rightText(1.2s, 1.6s);
}
&:nth-child(7) {
@include leftText(1.7s, 2.1s);
}
&:nth-child(8) {
@include rightText(1.7s, 2.1s);
}
}
}
& > .background {
width: 200vw;
height: 100vh;
position: absolute;
&:nth-child(3) {
background: #222;
top: -50%;
left: -50%;
transform-origin: center bottom;
rotate: 0deg;
@include rotate();
}
&:nth-child(2) {
background: #444;
top: 50%;
left: -50%;
transform-origin: center top;
@include rotate();
}
}
}
@keyframes rotate1 {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(180deg);
}
}
@keyframes rotate2 {
0% {
transform: rotate(180deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes rotate3 {
0% {
transform: rotate(360deg);
}
100% {
transform: rotate(540deg);
}
}
@keyframes rotate4 {
0% {
transform: rotate(540deg);
}
100% {
transform: rotate(720deg);
}
}
@keyframes rotate5 {
0% {
transform: rotate(720deg);
}
100% {
transform: rotate(900deg);
}
}
@keyframes inTop {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
@keyframes outTop {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
@keyframes inDown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
@keyframes outDown {
0% {
transform: translateY(0);
}
100% {
transform: translateY(100%);
}
}
異なったアニメーションを同時に設定することが出来ます
下記コードでいうと0.2秒後にrotate1
のアニメーションが開始されて0.6秒後にrotate2
のアニメーションが開始される...という具合になります
ただ注意しなければならないのがanimation-delay
を何も設定しないと同時に設定されている全てのアニメーションが実行されてしまい想定の動作をしなくなるので注意が必要です
@mixin rotate() {
animation: rotate1 0.2s ease-in-out 0.2s forwards,
rotate2 0.2s ease-in-out 0.6s forwards,
rotate3 0.2s ease-in-out 1.1s forwards,
rotate4 0.2s ease-in-out 1.6s forwards,
rotate5 0.2s ease-in-out 2.1s forwards;
}
文字と模様を組み合わせたアニメーション
アニメーションとしては文字が出現されると同時に波紋のような模様が表示されるものになります
<div class='animation2'>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class='text'>
<div>テ</div>
<div>キ</div>
<div>ス</div>
<div>ト</div>
</div>
</div>
.animation2 {
position: relative;
& > .circle {
scale: 0;
border-width: 10px;
&:nth-child(1) {
position: absolute;
top: -3rem;
left: -3rem;
width: 15rem;
height: 15rem;
border: solid 1rem #000;
border-radius: 100%;
transform-origin: center center;
box-sizing: border-box;
animation: scale 0.35s ease-in 0s forwards;
}
&:nth-child(2) {
position: absolute;
top: 5rem;
left: 6rem;
width: 15rem;
height: 15rem;
border: solid 1rem #000;
border-radius: 100%;
box-sizing: border-box;
animation: scale 0.35s ease-in 0.15s forwards;
}
&:nth-child(3) {
position: absolute;
top: -2rem;
right: 5rem;
width: 15rem;
height: 15rem;
border: solid 1rem #000;
border-radius: 100%;
box-sizing: border-box;
animation: scale 0.35s ease-in 0.35s forwards;
}
&:nth-child(4) {
position: absolute;
top: 3rem;
right: -2rem;
width: 15rem;
height: 15rem;
border: solid 1rem #000;
border-radius: 100%;
box-sizing: border-box;
animation: scale 0.35s ease-in 0.55s forwards;
}
}
}
.text {
font-size: 10rem;
font-weight: bold;
text-align: center;
margin-top: 2rem;
display: flex;
}
.text > div {
opacity: 0;
transform: scale(0.5);
&:nth-child(1) {
animation: pop-in 0.6s ease-in-out 0s forwards;
}
&:nth-child(2) {
animation: pop-in 0.6s ease-in-out 0.15s forwards;
}
&:nth-child(3) {
animation: pop-in 0.6s ease-in-out 0.35s forwards;
}
&:nth-child(4) {
animation: pop-in 0.6s ease-in-out 0.55s forwards;
}
}
@keyframes pop-in {
0% {
opacity: 0;
transform: scale(0.5);
}
50% {
opacity: 1;
transform: scale(1.2);
}
80% {
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes scale {
0% {
scale: 0;
border-width: 10px;
}
50% {
scale: 1;
border-width: 10px;
}
100% {
scale: 1;
border-width: 0px;
}
}
前述した2つのアニメーションよりもシンプルなコードにはなっています
自分の中では上位に好きなアニメーションです
参考