みかづきブログ その3

本ブログは更新を終了しました。通算140万ユーザーの方に観覧頂くことができました。長い間、ありがとうございました。

👆

引越し先はこちらです!

delegateを仕上げよう 2017

ライブラリは、jQuery、Vue、React、PixiJSなどなど、
タスクランナーは、Grunt、Gulp、webpackなどなど、
これまで、いろんなものをつかってウェブフロントの開発を行ってきましたが、

最近Noライブラリ、Noタスクランナーで、
普通にJavaScriptを書くことが増えております。(余談ですがCSSは面倒すぎるのでCompassをつかっています)

で、先日、生のJavaScriptを書いていたとき、
「ここはデリゲートで処理しよう」と思ったの瞬間があったですが、
ふと、昔自作したデリゲート関数の存在を思い出しました。

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com

(function(win, doc) {
    
    "use strict";
    
    var room = doc.querySelector("#room");
    
    delegate(room, "click", ".member", function() {
        alert(this.id);
    });
    
    function delegate(parent, eventName, selector, callback) {        
        parent.addEventListener(eventName, function(evt) {
           
            (function checkTarget(target) {
                if (target.matches(selector, parent)) {
                    callback.call(target);
                } else {
                    if (target === parent) {
                        return;
                    } else {
                        checkTarget(target.parentNode);
                    }
                }
            })(evt.target);

        }, false);
    }
    
})(this, document);

こちらが3年前につくったデリゲートの最終版のようだったので、
早速つかってみたのですが、

愕然としました。

おいおい。3年前の自分はこれで満足していたのかと。

そう。このコードには大きな問題点が隠されていたのです。

問題点1 targetにdocumentを渡すと、エラーを吐く

jQueryでいうところのlive的な処理を書くとエラーを吐きます。

 if (target.matches(selector, parent)) {

この部分、targetがmatchesを持っていることが期待されていますが、
documentはmatchesを持っていないからです。

解決策
 if (target.matches && target.matches(selector, parent)) {

という感じで、
targetがmatchesを持っていることを確認してから叩くというように改修しました。

問題点2 結局どの要素でイベントが発火したのかがわからない

evt.targetにはtargetで渡したDOMが入るので、結局どの要素でイベントが発火したのかがわからず困りました。

解決策
evt.delegateTarget = target;

evtにdelegateTargetというキーを追加して、イベントが発火したDOMを特定できるように改修しました。

問題点3 インデントがスペース4つ

時代を感じました。
3年前の僕はスペース4つ派だったようです。

解決策

いまの僕はスペース2つ派なので、インデントをスペース2つにしました。


完成したデリゲート関数

function delegate(parent, eventName, selector, callback) {
  parent.addEventListener(eventName, function(evt) {
    (function checkTarget(target) {
      evt.delegateTarget = target;

      if (target.matches && target.matches(selector, parent)) {
        callback.call(target, evt);
      } else {
        if (target === parent) {
          return;
        } else {
          checkTarget(target.parentNode);
        }
      }
    })(evt.target);
  }, false);
}

いかがでしょう。
自分で使うぶんには困ることがなくなりました。
3年後にまた確認してみようと思います。

JavaScriptでクラス定数を継承したいという想い

いままでクラス定数は static と get をつかって書いていました。

class Klass {
  static get CONST() {
    return {
      HOGE : 1,
      FUGA : 2,
      PIYO : 3
    };
  }
}

console.log(Klass.CONST.HOGE); // => 1

なんとなくCONSTというオブジェクトの中に定数を入れるという書き方をしていたのですが、
継承すると親クラスのものを辿れなくなるという問題点がありました。

class SuperKlass {
  static get CONST() {
    return {
      HOGE : 1,
      FUGA : 2,
      PIYO : 3
    };
  }
}

class SubKlass extends SuperKlass {
  static get CONST() {
    return {
        PIYO : 4,
        PON : 5
    };
  }
}

console.log(SubKlass.CONST.PIYO); // => 4
console.log(SubKlass.CONST.PON); // => 5
console.log(SubKlass.CONST.HOGE); // => undefined 1が出て欲しい!

今回はこれの解決策を考えます。


解決策1 CONSTオブジェクトを廃止する

class SuperKlass {
  static get HOGE() {
    return 1;
  }

  static get FUGA() {
    return 2;
  }

  static get PIYO() {
    return 3;
  }
}

class SubKlass extends SuperKlass {
  static get PIYO() {
    return 4;
  }

  static get PON() {
    return 5;
  }
}

console.log(SubKlass.PIYO); // => 4
console.log(SubKlass.PON); // => 5
console.log(SubKlass.HOGE); // => 1

ものすごく簡単に解決します。
が。個人的にはCONSTオブジェクトでまとめたい。
深い意味はないんですがCONSTオブジェクトでまとめたいのです。

解決策2 親のCONSTオブジェクトにマージしてリターンする

class SuperKlass {
    static get CONST() {
        return Object.assign(super.CONST || {}, {
            HOGE : 1,
            FUGA : 2,
            PIYO : 3
        });
    }
}

class SubKlass extends SuperKlass {
    static get CONST() {
        return Object.assign(super.CONST || {}, {
            PIYO : 4,
            PON : 5
        });
    }
}

console.log(SubKlass.CONST.PON); // => 5
console.log(SubKlass.CONST.PIYO); // => 4
console.log(SubKlass.CONST.HOGE); // => 1

Objectアサインをつかえば解決できます。
確実に親クラスが存在し、かつ親クラスがスタティックなCONSTオブジェクトを持っているのであれば、単純にマージすればよいのですが、そうとは限らないので、super.CONSTがFalsyな値の場合は空のオブジェクトを使うようにしています。

ここまでして、クラス定数をCONSTオブジェクトにまとめたいかと言われれば謎です。

YouTubeLiveでMacのウェブカメラで撮影している映像をライブ配信し、その様子をウェブページに埋め込んだプレイヤーに表示する方法

ひょんなことからYouTubeLiveをウェブページに埋め込む方法を調べました。

必要なもの

  1. Googleアカウント( https://accounts.google.com/SignUp
  2. Google Adsenseアカウント( https://www.google.co.jp/adsense/start/
  3. Adsenseアカウントに紐付いたYouTubeチャンネル( https://support.google.com/youtube/troubleshooter/7367438 )を参考に紐付けましょう )
  4. OBS( https://obsproject.com



配信手順

1. YouTubeの 「クリエイターツール」 > 「ライブストリーミング」 > 「今すぐ配信」 を選択

f:id:kimizuka:20171020195246p:plain

2. 「サーバー URL」と「ストリーミングキー」をメモ

f:id:kimizuka:20171020195516p:plain

3. 「動画再生ページを表示」をクリックし、動画再生ページを準備

f:id:kimizuka:20171020195851p:plain

4. OBSを起動してサーバー URL、ストリーミングキーを設定

f:id:kimizuka:20171020200621p:plain

  • 「設定」 > 「配信」 を表示。
  • 「配信種別」を「カスタムストリーミングサーバー」に設定。
  • 「URL」に「サーバー URL」を入力。
  • 「ストリームキー」に「ストリーミングキー」を入力。
  • 「OK」をクリック
5. ソースを追加

f:id:kimizuka:20171020200805p:plain

  • 「ソース」 > 「+」 > 「映像キャプチャデバイス」をクリック

f:id:kimizuka:20171020201430p:plain

  • 適当な名前をつけて「OK」をクリック

f:id:kimizuka:20171020201443p:plain

  • 「デバイス」にウェブカメラを選択して「OK」をクリック
6. 配信開始

f:id:kimizuka:20171020201916p:plain

  • 「トランジション」をクリック

f:id:kimizuka:20171020201930p:plain

  • 「配信開始」をクリック
7. 埋め込み用のiframeを取得
  • 動画再生ページにアクセス

f:id:kimizuka:20171020202315p:plain

  • 「共有」 > 「埋め込む」 をクリック

f:id:kimizuka:20171020202519p:plain

  • iframeの埋め込みタグをコピー


このように取得したiframeのコードをウェブページに貼り付けることによって、
無事にウェブページにMacのウェブカメラで撮影している映像のライブ中継を埋め込むことができました。

また、YouTubeのiframeAPIに動画再生ページのIDを渡せば、プレイヤーを生成することも可能で、
パラメータを渡すことによって、iOS10以降のiPhoneでインライン再生ができることも確認しました。

kimizuka.hatenablog.com



どこかで誰かの役に立てば幸いです。