7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CSSアニメーションは楽しい

Posted at

背景

業務にてHTMLとCSSをガッツリ触るタイミングがありました
久しぶりだったというのもありCSSアニメーションで遊びたいと思いました
CSSアニメーション遊んでいる中「こんなことが出来るんだ!」というものを少しでも広めらたらいいな、と思い記事を書く運びになりました

コードについて

学生時代にCSSアニメーションの神・yui540さんを知り、色々とCSSアニメーションを真似ていました
社会人になってからCSSアニメーションにあまり触っていなかったので読み辛いコードになっているかもしれません
温かい心で見ていただければ幸いです

CSSアニメーションについて

処理の種類として大まかに2つの処理があるかと思います

transition

:hover:activeなどの擬似クラスをトリガーとしてアニメーションを行う処理です
簡単に実装出来るため様々なサイトで見ます

animation

擬似クラスではなく自分自身で処理を行うタイミングを決めてアニメーションを行えます
複雑なアニメーションを実装出来ます
本記事ではこちらを多用しています

アニメーションの探し方

CSSアニメーションでどのようなものを作るかはYouTubeで

  • ゲームのOP映像
  • アニメのOP映像
  • 曲のMV

などから探す良いかと思います

基本的な動作

2つのボックスにそれぞれのアニメーションを動かしていきます
・1つ目のボックス: 色の変化
・2つ目のボックス: 拡大

html
<div class="example">
  <div class="animation__1"></div>
  <div class="animation__2"></div>
</div>
scss
.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__1animation__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;:変形の基準点を左上に設定

CleanShot 2025-02-02 at 19.53.04.gif

画面全体を動くアニメーション

複数のレイヤーが上下左右からスライドしながら表示される仕組みになっています。

html
<div class="animation1">
  <div>
    <div></div>
    <div></div>
  </div>
  <div></div>
  <div></div>
  <div></div>
</div> 
scss
$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)でアニメーションが行われます

scss
$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%);
  }
}

CleanShot 2025-02-02 at 19.56.28.gif

テキストへのアニメーション

テキストに対してアニメーションを行っていきます
予想ですがSVGで行ったほうが良いかも、と思っていますがまだSVGのアニメーションに慣れていないので使っておりません

html
 <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>
scss
@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を何も設定しないと同時に設定されている全てのアニメーションが実行されてしまい想定の動作をしなくなるので注意が必要です

scss
@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;
}

CleanShot 2025-02-02 at 19.55.24.gif

文字と模様を組み合わせたアニメーション

アニメーションとしては文字が出現されると同時に波紋のような模様が表示されるものになります

html
<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>
scss
.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つのアニメーションよりもシンプルなコードにはなっています
自分の中では上位に好きなアニメーションです

CleanShot 2025-02-02 at 19.58.46.gif

参考

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?