こんにちは。音声UIの開発をしている武田です。今年もAmazon Alexaのコンテストが開催されます。このコンテストで専用の賞まで用意されている今熱いデザイン言語、Alexa Presentation Languageでできることを紹介します。
はじめに
Amazon Alexaのスキル、「コーデ相談 by WEAR(以下、コーデ相談)」をリリースしました。アイテム名から色々なコーディネートを探せるシンプルなスキルですが、Alexa Presentation Language(以下、APL)でできることを精一杯詰め込んでいます。この記事では、APLを使ってどんなチャレンジをしているか紹介します。ソースコードなどの実装の詳細は個別の記事で解説していく予定です。
APLとは
APLはAlexaの音声でのコミュニケーションを補助するためのUIを記述する仕組みです。用いることで視覚的な表現だけではなく、JavaScriptのようなインタラクティブな表現が可能となります。公式ドキュメントはこちらです。
例えば、写真を中央に表示するレイアウトはこのようになります。
{ type: 'Container', item: { type: 'Image', id: 'image1', source: 'https://image-url', height: '100px', width: '100px' }, alignItems: 'center', justifyContent: 'center', height: '100vh', width: '100vw' }
レイアウトは使用するコンポーネントの種類(Container
やImage
)を指定することで組んでいきます。コンポーネントに指定できるプロパティ(alignItems
やheight
)はHTML/CSSに近いです。しかし、alignItems
はContainer
コンポーネントのみで指定できるといったようにコンポーネントごとの役割がはっきりと決まっています。
APLはレイアウト以外にもJavaScriptのような動的な制御が可能です。コンテンツの制御にはコマンドと呼ばれるものを用いて以下のように指定します。
{ type: 'Scroll', componentId: 'targetComponentId', distance: 1 }
コマンドもコンポーネントのように種類が存在しており、上記の例は指定したコンポーネント内をスクロールさせる命令です。コマンドはスキルの応答と同時に実行するか、タッチなどのインタラクションに合わせて実行できます。これから紹介する実装事例では、このコマンドをふんだんに使っていきます。
表示する際のトランジションを追加する
見ていただいた方が早いと思いますので、この処理が走っている検索結果の画面がこちらです。
普通に画像を表示しているだけに見えますが、以下のような処理を走らせています。
- 1番から順番に画像を表示
- 表示する際の透明度が連続的に変わるようにする
- 画像が表示されてから、それぞれの番号を表示
これらの処理はWebでしたら、CSSアニメーションで容易に実装可能ですが、APLでは連続的に値を書き換える便利な機能は存在しません。SetValue
というコマンドでプロパティの値を書き換えることは可能ですが、途中の値を0、0.1、0.2のように補完してくれる訳ではないのでいきなり最後の値に切り替わってしまいます。
そこでコーデ相談では、コンポーネントのスクロールイベントを監視し、スクロール位置をよしなに計算することで透明度の値を書き換えるようにしました。実装はこちらのブログを参考にしています。
Alexa Skill Teardown: Building the Interaction Model for the Space Explorer Skill
スクロールはコマンドの例で紹介したScroll
を使うとして、スクロールイベントの監視と透明度の更新はこのようにしています。
{ type: 'ScrollView', id: 'targetComponentId', item: { type: 'Container', height: '1000vh' }, onScroll: [ { type: 'SetValue', componentId: 'image1', property: 'opacity', value: '${event.source.value * 4}' }, ], height: '100vh' }
onScroll
はスクロール位置の更新に合わせて実行されるコマンドを定義できるプロパティです。スクロール位置が格納されているevent.source.value
を計算し値を更新することで、連続的に値が変動するアニメーションを実現できるようになります。
onScroll
には複数のコマンドを指定できますし、コマンドにはdelay
という発火を遅らせるプロパティも存在しています。これらを組み合わせることで検索時のトランジションを実装しました。
またこのonScroll
は画面に表示されていない要素がスクロールした場合でも、コマンドを発火してくれます。つまり透明度を0にしてユーザーに見えない状態でも大丈夫なので、コーデ相談ではトランジション専用のコンポーネントを用意して使っています。
トーストを実装する
コーデ相談はスキルの中に仲良し度という概念が存在しており、仲良し度の上昇を知らせるためにトーストを用いています。
今回実装したトーストの動きを分解すると以下のような処理になります。
- 下から移動して出てくる
- 透明度が連続的に変わる
- 少し経ったら消える
トランジションで紹介したSetValue
コマンドは、2019年5月時点ではopacity
とtext
プロパティのみが更新できます。つまり、コンポーネントの位置を変更することが非常に厳しいと言えます。ただ、トーストのような下から移動する動きはコンポーネント内をスクロールさせることによって、同じような見た目を実現することが可能です。スクロールで動かせばそのままonScroll
が使えるので、透明度の更新もトランジションの時と同じ方法で実現できます。
トーストのコンポーネントを簡単に書くとこのようになります。
{ type: 'ScrollView', item: { type: 'Container', id: 'notifyContainer', items: [ { type: 'Text', text: 'トーストのメッセージ' } ], paddingTop: '100px' // 最初はメッセージを見せないための空間 }, onScroll: [ { type: 'SetValue', componentId: 'notifyContainer', property: 'opacity', value: '${event.source.value}' } ], height: '100px' }
あとは、このScrollView
を表示のタイミングでスクロールさせてあげるだけです。
スクロールできる領域なのでユーザーが触ると動いてしまいますが、透明なコンポーネントを上に重ねればタッチイベントがスクロール領域に届かなくなるので大丈夫です。
ScrollView
と挙動が似ているPager
コンポーネントは、ユーザーのインタラクションを無視するようにできるプロパティが用意されています。しかし、横方向しか移動できないため今回のトーストでは活用できませんでした。
相槌を打つ
Alexaが反応を返すまでの流れは以下のような手順となります。
これは一般的な流れではありますが、外部のAPIを叩く場合などではサーバーの処理の時間が長くなり、ユーザーに返答が返るまでの時間が長くなってしまいます。そこでコーデ相談では、相槌のようにとりあえず一度返事をして、その後に重い処理を実行するようにしました。流れにするとこのようになります。
上記の流れでは空のリクエストを飛ばし直していますが、普通ですとユーザーが何かアクションを起こさなければサーバーにリクエストは飛びません。リクエストを飛ばすにはSendEvent
コマンドを実行すれば良いのですが、このSendEvent
は応答の際に実行するコマンドとして直接使用できません。そこでコーデ相談では、SetPage
コマンドとonPageChanged
プロパティを組み合わせてSendEvent
コマンドを実行するようにしています。SetPage
はPager
内で表示しているコンポーネントを切り替えるためのコマンドで、以下のようにonPageChanged
で指定したコマンドを発火させられます。
{ type: 'Pager', items: [ { type: 'Container' }, { type: 'Container' } ], onPageChanged: [ { type: 'SendEvent', arguments: [] } ] }
このPager
に対してページを切り替えるSetPage
コマンドを実行することで、サーバーにリクエストが送られるようになります。この流れにすることにより、全体的な処理が完了するまでの時間は伸びています。しかし人間と会話するときのように、まずは何かしらの返答を返すコミュニケーションが取れるようになったと感じています。
さいごに
全体的にだいぶ無理をした実装をしており、公式ブログで紹介されている方法を利用しているとはいえ無理矢理感は否めません。運用やサービスの信頼性を考えると不安がないと言えば嘘になります。例えばonScroll
やonPageChanged
は公式のドキュメントには載っていませんし、いつ仕様が変わるかわからないプロパティです。今回の実装は良くも悪くもAPLの限界を超えていると感じています。
しかし、ユーザーにとって良いであろう体験をプラットフォームが成熟していないからという理由で諦めたくもありませんでした。ユーザーからしたら、「プラットフォームがサポートしていない機能」も「無理をした不安定な実装」も関係ないからです。重要なのは使いにくかったりうまく動かない状況になったら、それを迅速に把握して修正することだと思います。情報のキャッチアップをして今の実装が明らかに適切でないならば直しますし、相槌をうたない方が使いやすいよね、となるなら一般的な流れに戻すつもりです。
ZOZOテクノロジーズのR&D新規開発チームでは今後普及するであろう技術を先行研究し、様々な技術を用いたサービスを開発しています。今回のAlexaの開発の様子はこちらの記事で詳しく話しています。より良いユーザー体験を提供するために、技術を駆使して最高のプロダクトを作りませんか?