4. App のライフサイクル

このステップのポイント

TextAlive App API を使って開発された Web アプリケーションは、次の図のような流れで実行されます。

graph LR %% node definitions init("new Player({ app: { ... } })") onAppReady([onAppReady]) onAppMediaChange([onAppMediaChange]) onAppParameterUpdate([onAppParameterUpdate]) onVideoLoad([onVideoLoad]) onSongLoad([onSongLoad]) onSongMapLoad([onSongMapLoad]) onLyricsLoad([onLyricsLoad]) onTextLoad([onTextLoad]) onFontsLoad([onFontsLoad]) %% onBackgroundGraphicsUpdate([onBackgroundGraphicsUpdate]) onVideoReady([onVideoReady]) onTimerReady([onTimerReady]) onSeek([onSeek]) onPlay([onPlay]) onPause([onPause]) onStop([onStop]) onTimeUpdate([onTimeUpdate]) %% init init --> onAppReady onAppReady --> onVideoLoad onAppReady -. user input .-> onAppMediaChange onAppMediaChange -->onVideoLoad %% from video load to video ready onVideoLoad --> onSongLoad %% onVideoLoad --> onBackgroundGraphicsUpdate subgraph song onSongLoad --> onSongMapLoad end subgraph lyrics onSongMapLoad --> onLyricsLoad onLyricsLoad --> onTextLoad end subgraph fonts onTextLoad --> onFontsLoad end onFontsLoad --> onVideoReady %% onBackgroundGraphicsUpdate --> onVideoReady %% %% parameter update onAppReady -. user input .-> onAppParameterUpdate onVideoReady --> onTimerReady %% %% playback onTimerReady --> onPlay onTimerReady --> onSeek onPlay --> onTimeUpdate onTimeUpdate --> onTimeUpdate onPlay --> onPause onPause --> onPlay onPlay --> onStop onStop --> onPlay

onAppReady

const p = new Player({ app: true }) のようにして初期化されたプレイヤーは、最初に TextAlive のホストを探します。ホストが存在する場合、ホスト側で楽曲の選択や再生操作が行われる可能性があります。つまり、 TextAlive App API を使うアプリケーション(TextAlive App)は、適当なタイミングで楽曲が切り替わったり、再生位置が変更されたりする可能性に備えておく必要があります。

ホストが存在するかどうかは onAppReady イベントで確定できます。

p.addListener({
  onAppReady: (app) => {
    if (app.managed) {
      // ホストが存在する
    }
  },
});

ホストは App に対して再生したい楽曲の URL をクエリパラメータ ta_song_url で渡します。 App 側では、とくに追加実装なしにクエリパラメータのパースが行われ、楽曲 URL が指定されている場合はその情報が自動的に読み込まれます。

楽曲 URL が指定されているかどうかは onAppReady イベントで判断できます。楽曲 URL が指定されていない場合のフォールバックもここで記述できます。

p.addListener({
  onAppReady: (app) => {
    if (app.songUrl) {
      // 楽曲URLが指定されている
    } else {
      // 指定されていない(フォールバックのURLを読み込む)
      p.createFromSongUrl("fallback url");
    }
  },
});

onAppMediaChange

ホストは App に対して再生したい楽曲の URL を更新することがあります。この際 App の再読み込みは行われません。 App 側では onAppMediaChange イベントが呼ばれるため、これで URL の変更を検知します。

onAppMediaChange(または onAppReady)イベント発行から次の onVideoReady イベント発行までの間は、楽曲地図と歌詞の情報が順次更新されていきます。この間「2. 楽曲情報を活用する 」で紹介した player.data.songMapplayer.video などのプロパティは古いもののままですが、適切に onSongMapLoad などのイベントリスナを設定することでいち早く新しい情報にアクセスできます。

p.addListener({
  onAppMediaChange: (mediaUrl) => {
    console.log("新しい再生楽曲が指定されました:", mediaUrl);
  },
});

onVideoReady

onAppReady または onAppMediaChange イベントに続いて楽曲の情報が読み込まれ、歌詞の有無などがチェックされると、 onVideoReady イベントが発行されます。

p.addListener({
  onVideoReady: (v) => {
    if (v.firstChar) {
      // 歌詞付き楽曲
    } else {
      // 歌詞のない楽曲
    }
  },
});

onTimerReady

onVideoReady イベントに続いて音源の再生準備が整うと onTimerReady イベントが発行されます。

再生状態の変更イベント

PlayerEventListener を登録することで再生状態に関するイベントを取得できます。

p.addListener({
  onPlay: () => console.log("再生開始"),
  onPause: () => console.log("再生一時停止"),
  onStop: () => console.log("再生終了(頭出し)"),
  onMediaSeek: (position) => console.log("再生位置の変更:", position, "ミリ秒"),
  onTimeUpdate: (position) =>
    console.log("再生位置のアップデート:", position, "ミリ秒"),
  onThrottledTimeUpdate: (position) =>
    console.log("再生位置のアップデート:", position, "ミリ秒"),
});

上記のうち onTimeUpdateonThrottledTimeUpdate は楽曲の再生中に定期的に呼ばれるもので、基本的に同じ役割です。ただし、無印は発火間隔がデフォルトで 50 ミリ秒程度、 Throttled は発火間隔が 100 ミリ秒程度となります。

これらの発火間隔はそれぞれ player.wait および Player クラスの初期化時に与えるオプション throttleInterval で調整できます。

Tips

onMediaSeekonTimeUpdateonThrottledTimeUpdate イベントの外で楽曲の精確な再生位置の情報を取得する方法については「3. アニメーション」の player.timer.position をご覧ください。

onAppParameterUpdate

App は初期化時に TextAlive ホスト側で調整可能なパラメタの情報を定義できます。

const p = new Player({
  app: {
    parameters: [
      {
        title: "フォントサイズ",
        name: "fontSize",
        className: "Slider",
        params: [0, 100],
        initialValue: 70,
      },
      {
        title: "テキスト色",
        name: "color",
        className: "Color",
        initialValue: { r: 31, g: 67, b: 145 },
        // initialValue: "#1f4391" // 文字列でも動作します
      },
      {
        title: "ダークモード",
        name: "darkMode",
        className: "Check",
        initialValue: false,
      },
      {
        title: "フォントのスタイル",
        name: "fontStyle",
        className: "Select",
        params: [
          ["sans-serif", "サンセリフ (ゴシック体)"],
          ["serif", "セリフ (明朝体)"],
        ],
        initialValue: "sans-serif",
      },
    ],
  },
});

ホスト側でパラメタの値が調整されるたびに onAppParameterUpdate イベントが呼ばれます。

p.addListener({
  onAppParameterUpdate: (name, value) => {
    // e.g. name: fontSize, value: 40
    // e.g. name: color, value: { r: 90, g: 0, b: 0 }
    // e.g. name: darkMode, value: true
    // e.g. name: fontStyle, value: "serif"
  },
});

パラメタの値は p.app.parameters (ParameterValues) でいつでもアクセスできます。ホスト側で一度もパラメタの値が調整されていない場合、返値は undefined になります。

console.log(p.app.parameters.fontSize);
// 40
console.log(p.app.parameters.color);
// { r: 90, g: 0, b: 0 }
console.log(p.app.parameters.darkMode);
// true
console.log(p.app.parameters.fontStyle);
// undefined // 一度もパラメタが調整されていない場合

Tips

TextAlive のパラメタ調整機能は、デバッグに役立つだけでなく、アプリのユーザが好みの演出を作り出す自由を与えるものです。ぜひ積極的に利用してみてください。

パラメタ調整を使ったサンプルは「textalive-app-dance」をご覧ください。

次のステップ