画面対応echoデバイスでアニメーション、SVG(っぽいの)に対応しているので試す(APL1.1Beta)

先日、alexaのもくもく会に参加させていただいた時にamazonのハタナカさんおっしゃっておりましたが、APLのversion1.1ではSVGが使用できたり、アニメーションに対応しているようです。

[Introducing Alexa Presentation Language 1.1 (Beta): Animation, Improved Tools, and the Alexa Design System](https://developer.amazon.com/ja/blogs/alexa/post/046ed81c-56fa-4c32-94a7-77dc81dcd498/introducing-alexa-presentation-language-1-1-beta-animation-improved-tools-and-the-alexa-design-system)

実は現在進行形で作成中のスキルの方向性に大きくかかわる話だったりします。アニメGIFも使えず、静止画限定だと思ってたのにこれは話が変わってくる。

なにはともあれ試してみるしかないでしょう。

# 検証環境

- Alexa hosted skill
 - node8

```json
  "dependencies": {
    "ask-sdk-core": "^2.0.7",
    "ask-sdk-model": "^1.4.1",
    "aws-sdk": "^2.326.0",
    "ask-sdk-s3-persistence-adapter": "^2.6.0"
  }
```

# その前に

APLの使い方を復習しときます。

## ごくシンプルなAPL

ちょーシンプルなAPLだとこんな感じ。
なんかdirectiveっていうdocumentの単位があって、例えば```Alexa.Presentation.APL.RenderDocument``` だとドキュメントを描画する系の塊。
そこに例えば[APL Textコンポーネント](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-text.html)を使うと画面に文字が書ける

```node:index.js
// This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');

// 起動時に呼ばれる
const LaunchRequestHandler = {
    canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
        const apl = require('./hello.json');
        const builder = handlerInput.responseBuilder
        .speak('アレクサクッサクサ');
        
        for (let i = 0; i < apl.directives.length; i++) {
            builder.addDirective(apl.directives[i]);
        }
        return builder.getResponse();
    }
};

const HelloWorldIntentHandler = {
    //略
};
const HelpIntentHandler = {
    //略
};
const CancelAndStopIntentHandler = {
    //略
};
const SessionEndedRequestHandler = {
    //略
};
const IntentReflectorHandler = {
    //略
};
const ErrorHandler = {
    //略
};

exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        HelloWorldIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler) // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
    .addErrorHandlers(
        ErrorHandler)
    .lambda();
```

```json:hello.json
{
    "document": {
        "type": "APL",
        "version": "1.1",
        "theme": "dark",
        "mainTemplate": {
            "parameters": [
                "payload"
            ],
            "items": [
                {
                    "type": "Text",
                    "text": "オッス！おら、アレクサ"
                }
            ]
        }
    },
    "datasources": {}
}
```

# シンプルなSVG

まずはシンプルなSVGを画面に書いてみる。
というかすみません、SVG, SVGって書いてきたけど厳密にはSVGじゃないです。
**Alexa Vector Graphics (a subset of the Scalable Vector Graphics standard)** との事で、AVGらしいです。なのでこれ以降、AVGと呼びます。
ちなみにわかりずらいのが、SVGに関してはmp3やjpgと違ってファイル1枚渡して終わりって感じじゃないっぽい。Vector画像ってxmlなわけだけど、xmlの要素をjsonに当て込まないといけないらしい。

https://developer.amazon.com/docs/alexa-presentation-language/apl-avg-format.html
（←2019/7/14時点だと日本語ページがなかった）

ひとまずちょーシンプルな四角を書いて見ます。
まあ、Vectorを素で書くとかまずないでしょうけども。

```json
{
    "directives": [
        {
            "type": "Alexa.Presentation.APL.RenderDocument",
            "token": "token",
            "document": {
              "type": "APL",
              "version": "1.1",
              "graphics": {
                "rectangle": {
                  "type": "AVG",
                  "version": "1.0",
                  "height": 100,
                  "width": 100,
                  "items": [
                    {
                      "type": "path",
                      "pathData": "M 80 20 L 80 80 L 20 80 L 20 20 z",
                      "stroke": "yellow",
                      "strokeWidth": "10",
                      "fill": "white"
                    }
                  ]
                }
              },
              "mainTemplate": {
                "parameters": [
                  "payload"
                ],
                "item": {
                  "type": "Container",
                  "direction": "row",
                  "items": {
                    "type": "VectorGraphic",
                    "source": "rectangle",
                    "width": 100,
                    "height": 100
                  }
                }
              }
            }
        }
    ]
}
```

![スクリーンショット 2019-07-15 0.20.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/50eea390-8606-24a0-8a36-75f4e535fb40.png)

# シンプルなアニメーション

なんか動かす時は```RendorDocument``` ではなくて```ExecuteCommands``` の方を使います。
なので描画と別々のdirectiveを渡す事になる。

https://developer.amazon.com/docs/alexa-presentation-language/apl-standard-commands.html#animate_item_command

```json:hello_avg.json
{
    "directives": [
        {
            "type": "Alexa.Presentation.APL.RenderDocument",
            "token": "token",
            "document": {
              "type": "APL",
              "version": "1.1",
              "graphics": {
                "rectangle": {
                  "type": "AVG",
                  "version": "1.0",
                  "height": 100,
                  "width": 100,
                  "items": [
                    {
                      "type": "path",
                      "pathData": "M 80 20 L 80 80 L 20 80 L 20 20 z",
                      "stroke": "yellow",
                      "strokeWidth": "10",
                      "fill": "white"
                    }
                  ]
                }
              },
              "mainTemplate": {
                "parameters": [
                  "payload"
                ],
                "item": {
                  "type": "Container",
                  "direction": "row",
                  "items": {
                    "type": "VectorGraphic",
                    "id": "vec1",
                    "source": "rectangle",
                    "width": 100,
                    "height": 100
                  }
                }
              }
            }
        },
        {
            "type": "Alexa.Presentation.APL.ExecuteCommands",
            "token": "token",
            "commands": [
                {
                    "type": "AnimateItem",
                    "easing": "ease-in-out",
                    "duration": 6000,
                    "componentId": "vec1",
                    "value": [{
                            "property": "opacity",
                            "to": 1
                        },
                        {
                            "property": "transform",
                            "from": [{
                                    "translateX": "100vw"
                                },
                                {
                                    "rotate": 720
                                }
                            ],
                            "to": [{
                                    "translateX": 0
                                },
                                {
                                    "rotate": 0
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
```

**vec1** ってIDを振った図形があって、それを**ease-in-out**で変化(？)。720度回転させつつ、100vwから0まで移動してます。6秒かけて。
なお**ease-in-out**は、cssでいう**cubic-bezier(0.42, 0.00, 0.58, 1.00)** と同等らしい。ベジェ曲線？あんまわからんけど。

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/8f04a100-a141-6dc2-211a-d649d8db15c5.gif)

## 少し追加

画像を1枚いれて、今度はそれも動かして見ます。
実行の制御をするときは**Sequential**や**Parallel**を指定して、順番に行うか並行でやるか指定するみたいですね。今回は並列にしてみよう。
あとついでにテキストも一つ加えました。

```json:apl_avg.json
{
    "directives": [
        {
            "type": "Alexa.Presentation.APL.RenderDocument",
            "token": "token",
            "document": {
              "type": "APL",
              "version": "1.1",
              "graphics": {
                "rectangle": {
                  "type": "AVG",
                  "version": "1.0",
                  "height": 100,
                  "width": 100,
                  "items": [
                    {
                      "type": "path",
                      "pathData": "M 80 20 L 80 80 L 20 80 L 20 20 z",
                      "stroke": "yellow",
                      "strokeWidth": "10",
                      "fill": "white"
                    }
                  ]
                }
              },
              "mainTemplate": {
                "parameters": [
                  "payload"
                ],
                "item": {
                  "type": "Container",
                  "direction": "row",
                  "width": "100vw",
			      "height": "100vh",
			      "position": "absolute",
                  "items": [{
                    "type": "VectorGraphic",
                    "id": "vec1",
                    "source": "rectangle",
                    "width": 100,
                    "height": 100
                  },
                  {
                    "type": "Image",
                    "id": "img1",
			        "source": "https://xxxxxxxx.amazonaws.com/image/kamekusa01.jpg",
			        "scale": "fill",
			        "position":"absolute",
			        "width": "300",
			        "height": "300"
                  },
                  { 
                    "type": "Text",
                    "text": "APLめんどいっす",
                    "id": "txt1"
                  }]
                }
              }
            }
        },
        {
            "type": "Alexa.Presentation.APL.ExecuteCommands",
            "token": "token",
            "commands":[{
                "type": "Parallel",
                "commands": [
                    {
                        "type": "AnimateItem",
                        "easing": "ease-in-out",
                        "duration": 6000,
                        "componentId": "vec1",
                        "value": [{
                                "property": "opacity",
                                "to": 1
                            },
                            {
                                "property": "transform",
                                "from": [{"translateX": "100vw"},{"rotate": 720}],
                                "to": [{"translateX": 0},{"rotate": 0}]
                            }
                        ]
                    },
                    {
                      "type": "AnimateItem",
                      "componentId": "txt1",
                      "easing": "linear",
                      "duration": 8000,
                        "value": [{
                                "property": "opacity",
                                "to": 1
                            },
                            {
                                "property": "transform",
                                "from": [{"translateY": "20vh"},{"scale": 1}],
                                "to": [{"translateY": "80vh"},{"scale": 2}]
                            }
                        ]
                    },
                    {
                      "type": "AnimateItem",
                      "componentId": "img1",
                      "easing": "linear",
                      "duration": 9000,
                        "value": [{
                                "property": "opacity",
                                "to": 1
                            },
                            {
                                "property": "transform",
                                "from": [{"translateX": "20vw", "translateY": "100vh"},{"scale": 0.5}],
                                "to": [{"translateX": "40vw", "translateY": "40vh"},{"scale": 1}]
                            }
                        ]
                    }
                ]
            }]
        }
    ]
}
```

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/05145266-3069-6645-02d1-ca36f1fb008c.gif)

レイアウトとか超適当です。
AVGというより画像を動かせるのはいいかもしんないですね。
AVGの方は、SVG画像食わせる方法ないんやろうか。
ないのであればSVGからAVGに変換するライブラリでもあればいいんだろうな。

それからなんとなく使ってたAPLが少しだけ深くわかった気がする。
副産物的にSetPageだとかの利用の仕方もなんとなくわかった。

# 参考
なお、alexa blogはもうちょっと詳しいし高度ですよ。あと公式ドキュメント。
https://developer.amazon.com/ja/blogs/alexa/post/046ed81c-56fa-4c32-94a7-77dc81dcd498/introducing-alexa-presentation-language-1-1-beta-animation-improved-tools-and-the-alexa-design-system
https://developer.amazon.com/docs/alexa-presentation-language/apl-standard-commands.html#animate_item_command
https://developer.amazon.com/docs/alexa-presentation-language/apl-vectorgraphic.html
https://developer.amazon.com/docs/alexa-presentation-language/apl-avg-format.html

（英語にしないと見れないページある）

あとSVG。
これも奥深い。。
https://developer.mozilla.org/ja/docs/Web/SVG/Tutorial
https://developer.mozilla.org/ja/docs/Web/SVG/SVG_animation_with_SMIL