JavaScript5(DOM操作、class)

テキストアニメーションから続く

1:DOM操作(アニメーションのための文字分割)

HTMLの記述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
  <link href="https://fonts.googleapis.com/css2?family=Teko:wght@500&display=swap" rel="stylesheet">
</head>
<body>
  <div id="container">
   <div class="animate-title inview">
    SUPER ANIMATION
   </div>
    <button onclick="document.querySelector('.animate-title').classList.toggle('inview');">Animation</button>
  </div>
<script src="main.js"></script>
</body>
</html>

mixinの記述

@mixin animation(
  $name,
  $duration: 1s,
  $timing-function: ease,
  $delay: 0s,
  $iteration-count: 1,
  $direction: normal,
  $fill-mode: forwards
) {
animation: {
  name: $name;
  duration: $duration;
  timing-function: $timing-function;
  delay: $delay;
  iteration-count: $iteration-count;
  direction: $direction;
  fill-mode: $fill-mode;
 }
}

SCSSの記述(テキストアニメーションのときに使ったのとほぼ同じ)

@import "mixin"; html { font-family: 'Teko', sans-serif; } body { margin: 0; } #container { position: relative; height: 100vh; background-color: teal; } .animate-title { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; opacity: 0; font-size: 2em; &.inview { opacity: 1; & .char { display: inline-block; @include animation( $name: kf-animate-chars, $duration: 0.5s, $timing-function: cubic-bezier(0.39, 1.57, 0.58, 1), $fill-mode: both ); @for $i from 1 through 20 { &:nth-child(#{$i}) { animation-delay: $i * 0.04s; } } } } } @keyframes kf-animate-chars { 0% { opacity: 0; transform: translateY(-50%); } 100% { opacity: 1; transform: translateY(0); } }

main.jsの記述

// DOMが生成されていることを担保
// document.addEventListener("DOMContentLoaded", function () {
// const el = document.querySelector(".animate-title");
// // 余分なスペース(空白)を取り除く
// // console.log(el.innerHTML.trim());
// const str = el.innerHTML.trim();
// let concatStr = '';

// for (let c of str) {
// // 空白(/\s+/でもOK)を置き換えて無効にする
// c = c.replace(' ', ' ');
// // 一般的な記法
// // console.log('<span class="char">' +c +'</span>');
// // `を使ったテンプレートリテラルの記法
// // console.log(`<span class="char">${c}</span>`);
// concatStr += `<span class="char">${c}</span>`;
// }
// el.innerHTML = concatStr;
// });

// reduceを使うときの記述
  document.addEventListener("DOMContentLoaded", function () {
// .animate-titleを取得
  const el = document.querySelector(".animate-title");
  console.log(el);
// Reduceを使う場合配列になおす必要があるので
  const str = el.innerHTML.trim().split("");
  console.log(str);
  el.innerHTML = str.reduce((acc, curr) => {
// スペースを開けたいので空白を に置き換える
  curr = curr.replace(/\s+/, " ");
  console.log(curr);
// テンプレートリテラルを用いて組み立てた文字列を返す
  return `${acc}<span class="char">${curr}</span>`;
// 初期値を空白にすることでaccを''currに最初の配列を取り出す
}, "");
  console.log(el.innerHTML);
});

// Reduceを使う場合concatStrを定義する必要がなくなった
// Reduceなので、できることが限定されているのでコードの意味が他の人にわかりやすい

console.logを使うと処理の流れがわかりやすい

期待していたものが生成された

テンプレートリテラルは読めるようになっておこう

2:classを用いるとき

HTMLの記述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
  <link href="https://fonts.googleapis.com/css2?family=Teko:wght@500&display=swap" rel="stylesheet">
</head>
<body>
  <div id="container">
  <div class="animate-title">
    PLAY ANIMATION
  </div>
  <div class="animate-title-2">
    ANIMATE STRING
  </div>
<button onclick="document.querySelector('.animate-title').classList.toggle('inview');">Animation</button>
</div>
<script src="main.js"></script>
</body>
</html>

mixinの記述

@mixin animation(
$name,
$duration: 1s,
$timing-function: ease,
$delay: 0s,
$iteration-count: 1,
$direction: normal,
$fill-mode: forwards
) {
animation: {
name: $name;
duration: $duration;
timing-function: $timing-function;
delay: $delay;
iteration-count: $iteration-count;
direction: $direction;
fill-mode: $fill-mode;
}
}

SCSSの記述

@import "mixin";
html {
  font-family: "Teko", sans-serif;
}
// ブラウザのデフォルトのマージンをけして余白をなくす
body {
  margin: 0;
}

#container {
  position: relative;
// 画面を縦いっぱいにする
  height: 100vh;
  background-color: teal;
}

.animate-title,
.animate-title-2 {
// position: absolute;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -50%);
  color: white;
  opacity: 0;
  font-size: 2em;

  &.inview {
// invuiewが振られたとき透明度を解除
    opacity: 1;

  & .char {
// 上から降りてくるようにするspanなのでinline-blockにしないとダメ

    display: inline-block;
// アニメーションの設定
@include animation(
$name: kf-animate-chars,
$duration: 0.5s,
$timing-function: cubic-bezier(0.39, 1.57, 0.58, 1),
$fill-mode: both
);
// for文を使って1から9までの処理をループさせているよ
@for $i from 1 through 20 {
&:nth-child(#{$i}) {
animation-delay: $i * 0.04s;
    }
   }
  }
 }
}

@keyframes kf-animate-chars {
0% {
  opacity: 0;
// 最初透明で上方向50%のところにいる
  transform: translateY(-50%);
}

100% {
// 透明化を解除して50%から原点に戻ることでアニメーションしているように見せる
  opacity: 1;
  transform: translateY(0);
 }
}

main.jsの記述

document.addEventListener('DOMContentLoaded', function () {
// newでインスタンスが生成されたときに発火
  const ta = new TextAnimation('.animate-title');
  const ta2 = new TextAnimation('.animate-title-2');
// 1秒後にアニメーションが開始される設定
  setTimeout(() => {
   ta.animate();
   ta2.animate();
  }, 1000);
});

class TextAnimation {
// クラスが初期化される段階で必ず呼ばれる関数
  constructor(el) {
// インスタンス化
    this.el = document.querySelector(el);
    console.log(el)
    console.log(this.el)
    this.chars = this.el.innerHTML.trim().split("");
    console.log(this.chars)
    this.el.innerHTML = this._splitText();
    console.log(this.el.innerHTML)
}
// プライベートメソッド(暗黙の了解で明示している)
  _splitText() {
    return this.chars.reduce((acc, curr) => {
      curr = curr.replace(/\s+/, ' ');
      return `${acc}<span class="char">${curr}</span>`;
  }, "");
}
// inviewのときアニメーションの発火、パブリックメソッド
  animate() {
    this.el.classList.toggle('inview');
 }
}

迷ったらconsole.logで確認すると処理の流れがわかりやすくなる

5.5に続きます

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です