6から続きます。
2:リファクタリング+機能追加
mixinはいつもどおりなので省略
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">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<section>
<div class="animate-title">Start Animation.</div>
</section>
<section>
<div class="animate-title">Use what you have.</div>
</section>
<section>
<div class="animate-title">Do what you can.</div>
</section>
<script src="text-animation.js"></script>
<script src="scroll-polyfill.js"></script>
<script src="scroll.js"></script>
<script src="main.js"></script>
</body>
</html>
<script src=”scroll-polyfill.js”></script>を追加
SCSSの記述
@import "mixin";
html {
font-family: "Teko", sans-serif;
}
body {
margin: 0;
}
section {
position: relative;
height: 100vh;
background-color: teal;
&:nth-child(2) {
background-color: mediumvioletred;
}
&:nth-child(3) {
background-color: orange;
}
}
.animate-title,
.tween-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;
}
}
& .char {
opacity: 0;
}
}
.animate-title.inview .char {
@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 30 {
&: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の記述
document.addEventListener("DOMContentLoaded", function () {
// コールバック関数を設ける
const cb = function (el, isIntersecting) {
if (isIntersecting) {
const ta = new TextAnimation(el);
ta.animate();
console.log(ta);
}
};
// 第1引数にエレメント第二引数にコールバック関数
const so = new ScrollObserver(".animate-title", cb);
console.log(so);
});
scroll.jsの記述
// スクロールの監視を定義する
class ScrollObserver {
// 第一引数にanimate-title第2引数にコールバック関数第三引数にオプション
constructor(els, cb, options) {
this.els = document.querySelectorAll(els);
console.log(this.els);
const defaultOptions = {
root: null,
rootMargin: "0px",
threshold: 0,
once: true
};
this.cb = cb;
console.log(this.cb);
// assignというメソッドででdefaultOptionsとoptionsをマージする
this.options = Object.assign(defaultOptions, options);
console.log(this.options);
this.once = this.options.once;
console.log(this.once);
this._init();
console.log(this._init);
}
// 初期化処理を記載して変数の記載漏れを防ぐ
_init() {
const callback = function (entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log(this);
this.cb(entry.target, true);
if (this.once) {
observer.unobserve(entry.target);
}
} else {
this.cb(entry.target, false);
}
});
};
// thisをbindで束縛するとScrollObserverのcb(コールバック関数)を参照する
this.io = new IntersectionObserver(callback.bind(this), this.options);
console.log(this.io)
// 後方互換性のためPolyfillの設定をする
this.io.POLL_INTERVAL = 100;
// 要素が複数あるためループで回して監視
this.els.forEach(el => this.io.observe(el));
}
// デストロイメソッドを追加して、監視をやめる
destroy() {
this.io.disconnect();
}
}
text-animation.jsの記述
// テキストアニメーションを定義
class TextAnimation {
constructor(el) {
this.DOM = {};
// elがDOMのときthis.Dom.elに格納、falseのときdocument.querySelector(el);でDOMを取得する
this.DOM.el = el instanceof HTMLElement ? el : document.querySelector(el);
console.log(this.DOM.el);
this.chars = this.DOM.el.innerHTML.trim().split("");
this.DOM.el.innerHTML = this._splitText();
console.log(this.DOM.el.innerHTML);
}
_splitText() {
return this.chars.reduce((acc, curr) => {
curr = curr.replace(/\s+/, ' ');
return `${acc}<span class="char">${curr}</span>`;
}, "");
}
animate() {
this.DOM.el.classList.toggle('inview');
}
}
class TweenTextAnimation extends TextAnimation {
constructor(el) {
super(el);
this.DOM.chars = this.DOM.el.querySelectorAll('.char');
}
animate() {
// inviewをつける
this.DOM.el.classList.add('inview');
// TweenMaxの書き方(toメソッド)対象となるDOMアニメーションの間隔
this.DOM.chars.forEach((c, i) => {
// オプションeaseファンクション,delayの間隔,オプション
TweenMax.to(c, .6, {
ease: Back.easeOut,
delay: i * .05,
// アニメーションの始まる状態
startAt: { y: '-50%', opacity: 0},
// 終了時の状態
y: '0%',
opacity: 1
});
});
}
}
-
document.querySelectorAll(els);でanimate-titleを取得したので出力された。
-
console.log(this.cb);
main.jsで定義したコールバック関数cbが入っているため。
-
console.log(this.options);でoptionsにdefaultoptionsの初期設定をマージしたため
-
console.log(this.once);onceをtrueに設定したため
-
console.log(this.io)でクラスのIntersectionObserverが呼び出されて、callback関数
を参照している
-
console.log(this._init);の部分で_init()が呼び出されている
-
console.log(so);の部分で、ScrollObserverが呼び出されていている。
-
console.log(this);の部分で、ここのthisは直近のScrollObserverを参照している。
-
console.log(this.DOM.el);の部分で、.animate-titleの部分を取得している
-
console.log(this.DOM.el.innerHTML);の部分で、加工されたテキストが出力されている。
-
console.log(ta);inviewが付加されて完成されたものが出力されている。
6.2に続きます