魔法少女サイト

これは、魔法少女になれなかった男の記録である。

ニートに学ぶCSS Animation演出講座 2時間目

あいさつ

f:id:yuki540com:20180303195711p:plain

ハロー、yuki540だよ!ピロリン

みんな、前回の記事は見てくれたかな?

見てない人は、ぜひ見て実践してみてね!

さてと、じゃあ今回は何をするか説明するね。

今回作るのは、こちら!!!

前回より長めのアニメーションだね。

あと、画面を広く使っているところと画像を一切使ってないところにも注目してほしいな。

これは、yuki540.com_version4.0.0の「共依存」でも使った演出だよ!

実は、一部「東京喰種:re テレビcm」のアニメーションを参考にしてるよ!


東京喰種:re 1巻テレビCM

自分でアニメーション演出を考えるときは、自分の好きな動画なんかから動かし方のヒントをもらうのがおすすめだよ!(僕の場合は、アニメopとかMADからヒントをもらってるよ!)

今回は、コードがちょっと多くなるから、しんどいかもしれないけど頑張って!

それでは、早速やっていきまーしょう!

やっていきまーしょう!

まず完成したものを見たいと思うので、完成品はこちら。(PCを想定して作っているのでPCで見てね)

見ましたね?はい、じゃあエディタを立ち上げて一緒にコードを書きましょう!

今回のアニメーションは#prologue#episode-1っていう風に物語っぽい感じにパートを区切ってます。

prologue

まずは、#prologueから作っていきましょう。

マークアップ

まずはマークアップからしてみましょう。

<main id="stage">
  <section id="prologue">
    <div class="word-1">
      <div><p></p></div>
      <div><p></p></div>
      <div><p></p></div>
      <div><p></p></div>
    </div>
    <div class="word-2">
      <div><p></p></div>
      <div><p></p></div>
      <div><p></p></div>
    </div>
    <div class="connect-line">
      <div class="line line-1"></div>
      <div class="line line-2"></div>
      <div class="point point-1"></div>
      <div class="point point-2"></div>
      <div class="point point-3"></div>
    </div>
  </section>
</main>

表示するテキストはお好きなものを!

ちょっと今回コードが多いので細かい説明は省きます...

スタイル

html, body {
  margin: 0;
  padding: 0;
}
p { margin: 0; }

#stage {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  min-width: 1100px; min-height: 650px;
  overflow: hidden;
}

/**
 * #prologue
 */
#prologue {
  position: absolute;
  top: 0; left: 0;
  width: 200%; height: 100%;
}


/*** 共通: .word-1, .word-2 ***/
#prologue .word-1,
#prologue .word-2 {
  position: absolute;
  top: calc(50% - 60px);
  height: 120px;
}
#prologue .word-1 div,
#prologue .word-2 div {
  float: left;
  width: 120px; height: 120px;
  border: dashed 1px #888;
  border-right: none;
  box-sizing: border-box;
}
#prologue .word-1 div p,
#prologue .word-2 div p {
  width: 100%; height: 100%;
  font-size: 80px;
  color: #777;
  text-align: center;
  line-height: 120px;
  opacity: 0;
}
#prologue .word-1 div:last-child,
#prologue .word-2 div:last-child { border-right: dashed 1px #888; }
#prologue .word-1:after,
#prologue .word-2:after {
  content: ""; display: block; clear: both;
}

/*** .word-1 ***/
#prologue .word-1 { left: calc(25% - 240px); }

/*** .word-2 ***/
#prologue .word-2 { right: 80px; }


/*** .connect-line ***/
#prologue .connect-line {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
}
#prologue .connect-line .point {
  position: absolute; top: calc(50% + 90px);
  width: 20px; height: 20px;
  background-color: #666;
  border-radius: 50%;
}
#prologue .connect-line .point-1 { left: calc(25% - 250px); }
#prologue .connect-line .point-2 { left: calc(25% + 230px); }
#prologue .connect-line .point-3 { right: 70px; transform: scale(0); }

#prologue .line {
  position: absolute;
  top: calc(50% + 99px);
  height: 1px;
  overflow: hidden;
}
#prologue .line:after {
  content: ""; display: block;
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-color: #666;
  transform: translateX(-100%);
}
#prologue .line-1 { width: 480px; left: calc(25% - 240px); }
#prologue .line-2 {
  width: calc(((50% - 480px) / 2) + (50% - 80px));
  right: 80px;
}

flexじゃなく、癖でfloat使っちゃうので「floatなんて使いたくない」って方はflexなりを使ってください。

↓ 実際にこんな風にレイアウトが組まれるようになっています。

左側

f:id:yuki540com:20180303205302p:plain

右側

f:id:yuki540com:20180303205312p:plain

注目してほしいところは

  • #prologuewidth200%であること
  • .line:after(擬似要素)が設定されていること

の2点です。

アニメーション

では、アニメーションをつけていきましょう。

/*** .word-1 ***/
#prologue .word-1 div:nth-child(1) p { animation: show-word 0.6s ease-out 0.0s forwards; }
#prologue .word-1 div:nth-child(2) p { animation: show-word 0.6s ease-out 0.2s forwards; }
#prologue .word-1 div:nth-child(3) p { animation: show-word 0.6s ease-out 0.4s forwards; }
#prologue .word-1 div:nth-child(4) p { animation: show-word 0.6s ease-out 0.6s forwards; }

/*** .line-1 ***/
#prologue .line-1:after { animation: draw-line 1.2s ease 0s forwards; }

/*** #prologue: ここで右側に移動する ***/
#prologue { animation: move-prologue 0.8s ease 1.2s forwards; }

/*** .line-2 ***/
#prologue .line-2:after { animation: slash-line 1.6s ease 1.2s forwards; }

/*** .point-3 ***/
#prologue .point-3 { animation: put-point 1s ease-out 2s forwards; }

/*** .word-2 ***/
#prologue .word-2 div:nth-child(1) p { animation: show-word 0.6s ease-out 1.6s forwards; }
#prologue .word-2 div:nth-child(2) p { animation: show-word 0.6s ease-out 1.8s forwards; }
#prologue .word-2 div:nth-child(3) p { animation: show-word 0.6s ease-out 2.0s forwards; }

/*** .word-2: ここで次のパートに繋ぐための回転 ***/
#prologue .word-2 { animation: rotate-word-2 0.4s ease 2.6s forwards; }

/*********************************************************************************
  keyframes
**********************************************************************************/
@keyframes show-word {
  0%   { transform: scale(0.4); opacity: 0; }
  70%  { transform: scale(1.05); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes draw-line {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(0%); }
}
@keyframes slash-line {
  0%   { transform: translateX(-100%); }
  50%  { transform: translateX(0%); }
  100% { transform: translateX(100%); }
}
@keyframes move-prologue {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}
@keyframes put-point {
  0%   { transform: scale(0); }
  70%  { transform: scale(2); }
  100% { transform: scale(0); }
}
@keyframes rotate-word-2 {
  0%   { transform: rotate(0deg); opacity: 1; }
  100% { transform: rotate(-90deg); opacity: 0; }
}

はい...泥臭い書き方になってきましたね...

そうなんです。css animation自体が今回のような長めのアニメーションを作るための機能でないため、どうしてもコードが煩雑になってしまいます。

僕はこの煩雑なコーディングを「丹精込めた手打ちkeyframes」と呼んでいます。

で、解説しますと、

まず、.word-1show-wordで0.2秒間隔を開けながら、アニメーションします。

f:id:yuki540com:20180304110155p:plain

それと同時に.word-1の下にある.line-1draw-lineで0.8秒かけながらアニメーションします。

ここで.line-1で線を引くのにwidthを変更せずに.line-1:aftertranslateXで右側に移動させることで線を引いているように見せているのは描画コストを下げるためです。

f:id:yuki540com:20180304103514p:plain

次に#prologue自体をmove-prologueで右側に移動させます。

これも同様にtranslateXを使ってできるだけ描画コストを下げます。

このようなダイナミックな画面移動が画面を広く使ったアニメーション演出をより盛り上げてくれます。

f:id:yuki540com:20180304105240p:plain

#prologueが移動すると同時に.line-2slash-lineでアニメーションさせます。

draw-lineと違い、translateX(-100%) ~ translateX(100%)で通り過ぎています。

通り過ぎることで、.point-3put-pointアニメーションに吸い込まれているような表現になります。

f:id:yuki540com:20180304111607p:plain

次は、word-1と同様に.word-2show-wordで0.2秒間隔を開けながらアニメーションさせます。

あとは、.word-2rotate-word-2で-90度回転させれば、prologueのパートは完成です。

episode-1

次は、#episode-1をやっていきましょう。

マークアップ

<main id="stage">
  <section id="prologue"><!-- 省略 --></section>
  <!-- ここから -->
  <section id="episode-1">
    <div class="message-panel">
      <div class="message">
        <p class="char"></p>
        <p class="char"></p>
        <p class="char"></p>
        <small class="text">ハロー、ミライアカリだよ!ピロリン</small>
      </div>
      <div class="message">
        <p class="char"></p>
        <p class="char"></p>
        <p class="char"></p>
        <p class="char"></p>
        <small class="text">CSS Animation演出講座、みんなも受けてね!</small>
      </div>
      <div class="message">
        <p class="char"></p>
        <p class="char"></p>
        <p class="char"></p>
        <p class="char"></p>
        <small class="text">それでは、やっていきまーしょう!</small>
      </div>
    </div>
  </section>
  <!-- ここまで -->
</main>

上記のものを追加してください。(これもテキストはお好きなものを)

スタイル

/**
 * episode-1
 */
#episode-1 {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
}
#episode-1 .message-panel {
  position: absolute;
  top: calc(50% - 180px); right: 80px;
  width: 360px; height: 360px;
  transform: rotate(90deg);
}
#episode-1 .message-panel .message {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
}
#episode-1 .message-panel .message .char {
  float: left;
  width: 170px; height: 170px;
  font-size: 150px;
  color: #333;
  text-align: center;
  line-height: 170px;
  opacity: 0;
}
#episode-1 .message-panel .message .char:nth-child(1),
#episode-1 .message-panel .message .char:nth-child(2) { margin-bottom: 20px; }
#episode-1 .message-panel .message .char:nth-child(2n-1) { margin-left: 20px; }
#episode-1 .message-panel .message .text {
  position: absolute;
  top: 165px; left: 40px;
  width: 320px; height: 30px;
  display: block;
  font-size: 14px;
  color: #555;
  line-height: 30px;
  opacity: 0;
}
#episode-1 .message-panel .message:after {
  content: ""; display: block;
  position: absolute;
  top: 0; left: 0;
  width: 15px; height: 100%;
  background-color: #5D97BD;
  opacity: 0;
}

レイアウトは以下のように組まれます。

f:id:yuki540com:20180304114622p:plain

特に説明することがないので、スタイルの説明は省きます。

アニメーション

/*** .message-panel ***/
#episode-1 .message-panel {
  animation:
    rotate-message-panel 0.7s ease 2.6s forwards,
    move-left-message-panel 0.5s ease 3.6s forwards,
    move-right-message-panel 0.5s ease 4.7s forwards;
}

/*** .message:nth-child(1):after ***/
#episode-1 .message-panel .message:nth-child(1):after {
  animation: show-bar 0.5s ease 2.8s forwards; }

/*** .char ***/
#episode-1 .message-panel .message:nth-child(1) .char:nth-child(1) {
  animation: show-char 0.35s ease 2.8s forwards; }
#episode-1 .message-panel .message:nth-child(1) .char:nth-child(2) {
  animation: show-char 0.35s ease 2.9s forwards; }
#episode-1 .message-panel .message:nth-child(1) .char:nth-child(3) {
  animation: show-char 0.35s ease 3.0s forwards; }

/*** .text ***/
#episode-1 .message-panel .message:nth-child(1) .text {
  animation: show-text 0.5s ease 3.1s forwards; }

/*** .message:nth-child(1) ***/
#episode-1 .message:nth-child(1) { animation: fadeout 0.5s ease 3.6s forwards; }

/*** .message:nth-child(2):after ***/
#episode-1 .message-panel .message:nth-child(2):after {
  animation: show-bar 0.5s ease 3.8s forwards; }

/*** .char ***/
#episode-1 .message-panel .message:nth-child(2) .char:nth-child(1) {
  animation: show-char 0.35s ease 3.8s forwards; }
#episode-1 .message-panel .message:nth-child(2) .char:nth-child(2) {
  animation: show-char 0.35s ease 3.9s forwards; }
#episode-1 .message-panel .message:nth-child(2) .char:nth-child(3) {
  animation: show-char 0.35s ease 4.0s forwards; }
#episode-1 .message-panel .message:nth-child(2) .char:nth-child(4) {
  animation: show-char 0.35s ease 4.1s forwards; }

/*** .text ***/
#episode-1 .message-panel .message:nth-child(2) .text {
  animation: show-text 0.5s ease 4.2s forwards; }

/*** .message:nth-child(2) ***/
#episode-1 .message:nth-child(2) { animation: fadeout 0.5s ease 4.7s forwards; }

/*** .message:nth-child(3):after ***/
#episode-1 .message-panel .message:nth-child(3):after {
  animation: show-bar 0.5s ease 4.9s forwards; }

/*** .char ***/
#episode-1 .message-panel .message:nth-child(3) .char:nth-child(1) {
  animation: show-char 0.35s ease 4.9s forwards; }
#episode-1 .message-panel .message:nth-child(3) .char:nth-child(2) {
  animation: show-char 0.35s ease 5.0s forwards; }
#episode-1 .message-panel .message:nth-child(3) .char:nth-child(3) {
  animation: show-char 0.35s ease 5.1s forwards; }
#episode-1 .message-panel .message:nth-child(3) .char:nth-child(4) {
  animation: show-char 0.35s ease 5.2s forwards; }

/*** .text ***/
#episode-1 .message-panel .message:nth-child(3) .text {
  animation: show-text 0.5s ease 5.3s forwards; }

/********************************************************************************
  keyframes
*********************************************************************************/
@keyframes fadeout {
  0%   { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes fadein {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}
@keyframes rotate-message-panel {
  0%   { transform: rotate(90deg); }
  60%  { transform: rotate(-5deg); }
  100% { transform: rotate(0deg); }
}
@keyframes show-char {
  0%   { transform: translateX(100px); opacity: 0; }
  100% { transform: translateX(0); opacity: 1; }
}
@keyframes show-text {
  0%   { transform: translateX(-100px); opacity: 0; }
  100% { transform: translateX(0); opacity: 1; }
}
@keyframes show-bar {
  0%   { width: 300px; opacity: 0; }
  100% { width: 15px; opacity: 1; }
}
@keyframes move-left-message-panel {
  0%   { right: 80px; }
  100% { right: calc(100% - 450px); }
}
@keyframes move-right-message-panel {
  0%   { right: calc(100% - 450px); }
  100% { right: 80px; }
}

さっきから意味不明なキーフレーム名ですが、単に僕の英語力と英単語のバリエーションがないだけなので、自分でお好きなキーフレーム名を設定していただいて大丈夫です...

で、解説しますと、

まず、.message-panelrotate-message-panelで前のパートの回転を引き継いでいるように見せます。

f:id:yuki540com:20180304122905p:plain

で、.message-panelに複数のキーフレームをカンマ区切りで指定していますね。

実は、animationプロパティは複数のキーフレームを適用することができます。

これを使うことでアニメーションを繋げていくこともできますし、複数のキーフレームを同時に適用することもできます。

回転しながら、.message:aftershow-barでアニメーションさせ、.charshow-charで0.1秒間隔を空けながらアニメーションさせます。

その次に.textshow-textで0.5秒かけてアニメーションします。

↓ 言葉で説明するのが難しいですが、こんな感じになります。

回転しすぎて、バウンドしているようにみせることで、より気持ちのいいアニメーションになります。

後は、.message-panelmove-left-message-panelで左側に移動させながら、.message:nth-child(1)fadeoutで非表示して、.message:nth-child(2)側にアニメーションを引き継がせるだけです。

さっきと同じ書き方なので説明は省きます。

違いは、.message-panelが移動しているだけです。

終わり。

どうでしょうか?動きましたか?

コードが多めなのに説明が雑ですみません...

完成したコードはこちらにあるので、うまくいかない方はご覧ください。

おわりに

今回も説明が下手 & 説明が雑で読みづらかったと思いますが、最後までみてくださりありがとうございました。

画面を広く使うことでアニメーションに迫力が出ることと、画像を使わなくても楽しい表現ができることがちょっとでも伝わってくれたなら幸いです。

これからも毎週日曜更新で続けていきますので、よかったらまたみてください。

じゃーねー!

f:id:yuki540com:20180304131107j:plain