テキストアニメーションから続く
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で確認すると処理の流れがわかりやすくなる