MENUCLOSE
twitter ツイッターfeedlyRSS

オンスタでは、オンラインで学習するために役立つコンテンツを掲載しています。そのほか、UdemyをはじめとしたEラーニングサービスの紹介も行っています。

オンスタ | オンラインで学習するための近道になるサイト
[JavaScript_中級者への道] プロトタイプについて – [3] 身近な機能を実装してみる

[JavaScript_中級者への道] プロトタイプについて – [3] 身近な機能を実装してみる

公開日:2020年02月19日
最終更新日:2020年02月19日
オンスタ運営
プログラミング
ホーム > 全記事一覧 > 制作 > エンジニア > プログラミング > [JavaScript_中級者への道] プロトタイプについて – [3] 身近な機能を実装してみる
このエントリーをはてなブックマークに追加
[JavaScript_中級者への道] プロトタイプについて – [3] 身近な機能を実装してみる
【PR】Udemyならスマホでもタブレットでも、いつでも気軽に学べます。

それでは、プロトタイプを利用したクラスについて少し学んだ所で、身近な機能について実装してみましょう。

 

左右にドアが開くアニメーションを実装してみる

ところが、学んだことを実際の課題で利用するというのが最大のハードルです。

いくら自己啓発本を読んだとしても、実際の生活で活かすのは難しいものです。

 

クラスの実装は、簡単なアニメーションなどでも応用することが可能です。

ここではページを開いたら、左と右にパネルが時間差で移動する簡単なアニメーションを実装してみましょう。

 

完成品

https://codepen.io/1shiharaT/pen/NWPZPKQ

See the Pen 研修_ドアアニメーションデモ by 1shiharaT (@1shiharaT) on CodePen.

 

まずはHTMLとCSSから

HTMLは、左と右にパネルが移動するので、単純なHTMLで問題なさそうです。

FLOCSSで書いてみます。

<div class="js-introduction">
  <div class="js-introduction__left"></div>
  <div class="js-introduction__right"></div>
</div>

CSS左と右にパネルを配置して、それぞれ is-open というクラスを当てたら左右に移動するようにしてみます。

.js-introduction {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  &.is-loaded {
    display: none;
  }
  &__left {
    position: absolute;
    left: 0;
    width: 50vw;
    height: 100vw;
    background: #000;
    transition: all ease .5s;
    &.is-open {
      left: -50vw;
      transition: all ease .5s;
    }
  }
  &__right {
    position: absolute;
    right: 0;
    width: 50vw;
    height: 100vw;
    background: #ccc;
    transition: all ease .5s;
    &.is-open {
      right: -50vw;
      transition: all ease .5s;
    }
  }
}

.js-introduction-reset {
  display: block;
  margin-left: auto;
  margin-right: auto;
  width: 300px;
  background: #224555;
  color: #fff;
  padding: 10px 24px;
  font-size: 18px;
  border: none;
  text-decoration: none;
  text-align: center;
  margin-top: 45vh;
}

上記は完成形のCSSを記述していますが、実際の実装時は後述のJavaScriptと行ったり来たりしながら実装します。

主な仕様

  • アニメーションを実行する要素は .js-introduction
  • .js-introduction 内にはそれぞれ左右に開く .js-introduction__left、.js-introduction__right という要素を配置
  • .js-introduction__left、.js-introduction__right は is-open という class が付与されるとアニメーションが実行される

 

JavaScriptを記述してみる

さて本題のJavaScriptの実装です。

今回はせっかくなのでバニラJS(jQuery等を利用しない素のJavaScript)で書いてみます。

1. まずはコンストラクタから実装する

とりあえずコンストラクタを定義します。

コンストラクタって何?という方は、前回の記事を参照ください。

new 演算子をつけて呼び出した時に最初に実行される関数です。

// コンストラクタ
function Introduction() {

}

// jQueryの $(function(){]) と同じような意味合いのイベント
// DOMが生成された後に実行
addEventListener("DOMContentLoaded", function () {
  var introInstance = new Introduction();
});

Introduction という関数を定義し、new 演算子をつけることで introInstance は Introductionのインスタンスとして生成されます。つまり、Introductionの prototype を継承します。

 

2. 設定値を考えてみよう

さて、期待するアニメーションを実装するのにどんな情報が必要になるでしょうか?

要件を満たすために必要な情報

  • アニメーションを実行するDOM要素
    • それを取得するのに必要なセレクタ
  • 左のドアパネルのDOM要素
  • 右のドアパネルのDOM要素
  • 左のドアが開いてから、右のドアが開くまでの時間
  • 左右のドアにアニメーションを実行する時に付与するclass

とはいえいきなりすべてを事前に考えきるのは難しいものです。これも実装を進める中で「これは設定値としてクラスの中で参照できるように this に格納した方がいいのではないか」という考えから後付で設定することも多々あります。

 

ポイントとしては、【1回以上参照する可能性があるか】【後から変更する確率が高いか】です。

 

検討した設定値をコンストラクタ内に this のプロパティとして定義します。

そうすることで、今後定義するクラスのprototypeに紐付けたメソッド内で、簡単に参照することができます。

function Introduction() {
  this.options = {
    // セレクタ
    targetClassName: ".js-introduction",
    // 左のドアが開いてから右のドアがあくまでの時間
    openDelay: 1000,
    // 開いているドアに付与するclass名
    doorActiveClassName: 'is-open'
  }
  // domを取得しセット
  this.el = document.querySelector(this.options.targetClassName);

  // 左、右のドアを格納
  this.elDoors = {
    left: this.el.querySelector(this.options.targetClassName + "__left"),
    right: this.el.querySelector(this.options.targetClassName + "__right")
  }
  return this;
}

addEventListener("DOMContentLoaded", function () {
  var introInstance = new Introduction();
});

これで、このアニメーションで利用する値がどこで定義されているか一目瞭然です。

 

3. 機能を考えてみる

次は、アニメーションを実装する上で必要になりそうな機能を考えてみます。

左右のドアが時間差で移動するというシンプルなアニメーションなので、次の機能があれば実装できそうです。

 

  • ドアを開く機能 == is-open というclassを付与する機能
function Introduction() {
  this.options = {
    // セレクタ
    targetClassName: ".js-introduction",
    // 左のドアが開いてから右のドアがあくまでの時間
    openDelay: 1000,
    // 開いているドアに付与するclass名
    doorActiveClassName: 'is-open'
  }
  // domを取得しセット
  this.el = document.querySelector(this.options.targetClassName);

  // 左、右のドアを格納
  this.elDoors = {
    left: this.el.querySelector(this.options.targetClassName + "__left"),
    right: this.el.querySelector(this.options.targetClassName + "__right")
  }
  return this;
}

// ドアを開くメソッドの定義
Introduction.prototype.start = function(){
    // 注意 : setTimeoutやイベントなど、コールバックを指定する場合、スコープが区切られるため
    // 変数等に代入する必要があります。
    var self = this;
    setTimeout(function(){
        self.elDoors.left.classList("is-open");
        setTimeout(function(){
          self.elDoors.left.classList("is-open");
        }, 1000);
    }, 1000);
}

addEventListener("DOMContentLoaded", function () {
  var introInstance = new Introduction();
  // 呼び出す
  introInstance.start();
});

ここで注意したのが、メソッド内で関数を定義する時です。

関数を定義するとスコープが区切られるため、関数内でthisの参照先がwindowになってしまいます。

これではせっかく洗い出してthisに格納した値が取得できないので、selfといった変数にthisを代入し、間接的に参照するようにします。

※ ES6等ではアロー関数 ( () => {}  )という、thisを束縛しない仕組みが追加されています。

 

4. メソッドを切り分けてみる

さて、晴れてアニメーション自体は実装することができましたが、まだ改善の余地があります。

start メソッドでは、次の似たようなコードが2回呼び出されています。

self.elDoors.left.classList("is-open");
self.elDoors.right.classList("is-open");

DRY (Don’t Repeat Yourself : 一度書いたことは二度と書かない)の精神で、上記のコードを別のメソッドとしてまとめてみます。

~~~~略~~~~ 
Introduction.prototype.start = function(){
    var self = this;
    setTimeout(function(){
        self.open("left")
        setTimeout(function(){
          self.open("right")
        }, 1000);
    }, 1000);
}
/**
 * ドアを開く
 * @param string target left or right
 * @return this
 */
Introduction.prototype.open = function (target) {
  if (typeof this.elDoors[target] !== "undefined") {
    this.elDoors[target].classList.add("is-open");
  }
  return this;
}
~~~~略~~~~

新たに open というメソッドを定義し、this.elDoorsというプロパティに引数に渡された値のプロパティがあったら、is-openというclass属性を追加。start メソッドでは、open メソッドを呼び出す形に変えています。

一見、さほど変わらないかもしれませんが、例えば次のようなケースで役に立ちます

 

  • is-open というclass属性の他に追加したい属性が出てきた場合
  • 扉を4つに分割する必要が出てきた場合

 

などなど…

 

二度、三度利用する可能性のある機能は、メソッドとして切り分けて定義することで、その後の

汎用性が格段にあがります。

 

5. 他の機能もメソッドとして追加してみる

さて、開発中に追加要件が発生することはよくあることです。

むしろ、当初の要件で完了まで進むことが少ないかもしれません。

 

追加の要件として、閉じるボタンをクリックしたら、ドアが閉じ、再度アニメーションが再開するように改修を追加してみましょう。

HTML

<!DOCTYPE html>
<html lang="ja">
<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>デモ</title>
</head>
<body>
  <div class="js-introduction">
    <div class="js-introduction__left"></div>
    <div class="js-introduction__right"></div>
  </div>
  <!--追加 : 開始-->
  <a href="#" class="js-introduction-reset">リスタート</a>
  <!--追加 : ここまで-->
</body>
</html>

JavaScript

~~~~略~~~~ 
/**
 * 閉じるメソッド
 * @param string target left or right
 * @return this
 **/
Introduction.prototype.close = function (target) {
  if (typeof this.elDoors[target] !== "undefined") {
    // class属性の is-open を削除
    this.elDoors[target].classList.remove("is-open");
  }
  return this;
}

/**
 * リセットする
 * @return this
 **/
Introduction.prototype.reset = function () {  
  this.close("left");
  this.close("right");
  this.el.classList.remove('is-loaded')
  return this;
}

addEventListener("DOMContentLoaded", function () {
  var introInstance = new Introduction(".js-introduction");
  introInstance.start();
  // 閉じるボタンをクリックしたときの処理を追加
  document.querySelector(".js-introduction-reset").addEventListener('click', function(e){
    e.preventDefault();
    introInstance.reset();
    introInstance.start();
  });
});

クラスとしてメソッドを追加することで、このような改修についてもすでにクラス内で利用しているメンバ変数 (クラス内で this に紐付けられている変数の呼称) やメソッドを利用でき、コードとしてもシンプルに記述することができます。

まとめ

クラスの定義を一通り行いましたが、次の流れで実装することが可能です。

  1. コンストラクタの実装
  2. 設定値を検討 : メンバ変数の検討
  3. 機能の検討 : メソッドの定義
  4. リファクタリング : メンバ変数、メソッドを再検討

 

番外編 : Promiseを使ってみる

/**
 * ドアを開く
 * @param string target left or right
 * @return Promise
 */
Introduction.prototype.open = function (target,timeout) {
  return new Promise( (resolve, reject ) => {
    if (typeof this.elDoors[target] !== "undefined") {
      setTimeout(()=>{
        this.elDoors[target].classList.add("is-open");
        resolve();
      }, timeout)      
    }
    reject();
  })
  return this;
}

/**
 * アニメーションを開始
 **/
Introduction.prototype.start = function () {  
  var self = this;
  this.open('left', 1000).then(()=>{
    this.open('right', 1000).then(()=>{
      setTimeout(function () {
        this.el.classList.add('is-loaded')
      }, 1000);
    });
  })
  return this;
}

/**
 * await を利用した場合
 **/
Introduction.prototype.start = async function () {  
  var self = this;
  await this.open('left', 1000)
  await this.open('right', 1000)
  setTimeout(function () {
    this.el.classList.add('is-loaded')
  }, 1000);
  return this;
}

参考

https://www.yunabe.jp/docs/javascript_class_in_google.html

 


【PR】Udemyならスマホでもタブレットでも、いつでも気軽に学べます。

Udemyを実際に体験した方の感想記事もぜひご覧ください♪

関連コンテンツ

[JavaScript_中級者への道] Js_Promiseを使ってみる
プログラミング
公開日: 2020年02月21日
最終更新日: 2020年02月21日
[JavaScript_中級者への道] Js_Promiseを使ってみる
  • # ES6
  • # JavaScript
  • # Promise
  • # コールバック
オンスタ運営
【Vue.js】初心者のワタシがつまづいた。Sassの共通変数をグローバルに読み込む方法
プログラミング
マークアップ
公開日: 2020年01月16日
最終更新日: 2020年01月16日
【Vue.js】初心者のワタシがつまづいた。Sassの共通変数をグローバルに読み込む方法
  • # Sass
  • # vue-cli
  • # Vue.js
  • # vuejs
オンスタ運営
JS初心者必見!JavaScriptの勉強ができるWEBサービス紹介【タイプ別】
プログラミング
マークアップ
公開日: 2018年12月28日
最終更新日: 2019年05月28日
JS初心者必見!JavaScriptの勉強ができるWEBサービス紹介【タイプ別】
オンスタ運営
【JSプラグイン】スクロール連動型アニメーション!jQuery不要のJSプラグイン「AOS」!
Webデザイン
プログラミング
公開日: 2017年07月17日
最終更新日: 2019年05月28日
【JSプラグイン】スクロール連動型アニメーション!jQuery不要のJSプラグイン「AOS」!
オンスタ運営
Scratchなら遊び感覚で学べる!サービスの特徴やメリットをピックアップ
プログラミング
公開日: 2019年08月04日
最終更新日: 2019年08月01日
Scratchなら遊び感覚で学べる!サービスの特徴やメリットをピックアップ
  • # Scratch
オンスタ運営
【第1回】ノンプログラマー/プログラム初心者も楽しめる!ゴリラにも伝わるprocessing入門
エンジニア
プログラミング
公開日: 2016年12月06日
最終更新日: 2019年05月28日
【第1回】ノンプログラマー/プログラム初心者も楽しめる!ゴリラにも伝わるprocessing入門
オンスタ運営
Web業界で欠かせないスキル!HTML5プロフェッショナル認定資格とは?
オンライン学習
プログラミング
公開日: 2019年06月25日
最終更新日: 2019年07月11日
Web業界で欠かせないスキル!HTML5プロフェッショナル認定資格とは?
  • # HTML5プロフェッショナル認定資格
オンスタ運営
【最新】Pythonの学習におすすめの本全部で14選
プログラミング
制作
電子書籍
公開日: 2019年06月25日
最終更新日: 2019年09月03日
【最新】Pythonの学習におすすめの本全部で14選
  • # Python
  • # おすすめ
  • # 本
オンスタ運営
残り5時間!Udemyで新規受講生向けのセール実施中!新規ユーザー向けに対象コースが¥1,320から
エンジニア
オンライン学習
オンライン学習キャンペーン情報
デザイナー
デザイン
プログラミング
制作
公開日: 2019年11月07日
最終更新日: 2019年11月07日
残り5時間!Udemyで新規受講生向けのセール実施中!新規ユーザー向けに対象コースが¥1,320から
  • # Python
  • # Udemy
  • # セール情報
  • # タイムセール
オンスタ運営
【PHP】PHPでライブラリ管理!「Composer」を使ってみよう
エンジニア
プログラミング
公開日: 2018年12月06日
最終更新日: 2019年05月28日
【PHP】PHPでライブラリ管理!「Composer」を使ってみよう
オンスタ運営
全記事一覧

web制作者にオススメのオンライン学習サイト「Udemy」

世界最大級のコースがラインナップ!電子書籍サイト「Udemy」

世界最大級のコースがラインナップ!オンライン学習サイト「Udemy」

「Udemy」ではいつでもどんなトピックでも、専門家が教える何千ものコースを用意されており、無料で学べるコースも多数あります。もうちょっと勉強したいな。スキルアップしたいな。と思ったら是非チェックしてみてください。