ティックイベントハンドラー



ティックイベントハンドラー

ティックイベントは、時間が経過すると発生します。handleTickイベントハンドラーを使用して、ティックイベントを取得し、時間に基づいて定期的にコマンドを実行できます。

例えば、以下のTouchWrapperは、コンポーネントがpressed状態にある限り、リピートカウンターを約100ミリ秒ごとにインクリメントします。ユーザーがコンポーネントを解放し、pressed状態が終了すると、onPressハンドラーはカウンターをゼロにリセットします。

{
  "type": "TouchWrapper",
  "bind": [
    {
      "name": "RepeatCounter",
      "value": 0
    }
  ],
  "handleTick": {
    "when": "${state.pressed}",
    "minimumDelay": 100,
    "commands": {
      "type": "SetValue",
      "property": "RepeatCounter",
      "value": "${RepeatCounter + 1}"
    }
  },
  "onPress": {
    "type": "SetValue",
    "property": "RepeatCounter",
    "value": 0
  },
  "items": {
    "type": "Text",
    "text": "${RepeatCounter}カウントこのボタンをホールドしました"
  }
}

ティックイベントハンドラー

ティックハンドラーは、コンポーネントレベルまたはドキュメントレベルで定義できます。

すべてのコンポーネントhandleTickプロパティがあります。APLドキュメントには、最上位のhandleTickプロパティもあります。

プロパティ デフォルト スタイル設定 動的 説明
handleTick ティックハンドラーの配列 [] ティックイベントが発生したときに実行するハンドラーです。

単一のティックハンドラーは、以下のプロパティを持つオブジェクトです。

プロパティ デフォルト 説明
commands コマンド配列 必須 このハンドラーが呼び出された場合に実行するコマンドです。
description 文字列 "" このティックハンドラーの説明です(任意)。
minimumDelay 数値 1000 このハンドラーが再度呼び出される前に経過する必要がある最小期間(ミリ秒)です。
when ブール値 true trueの場合、このハンドラーが呼び出されます。

ティックハンドラーの配列は並列に実行されます。各ティックハンドラーには、when句があります。ティックハンドラーは、次の条件の両方が満たされたときに実行されます。

  • ハンドラーのwhen句がtrueと評価されたとき。
  • このティックハンドラーが最後に実行されてから、minimumDelayミリ秒以上経過したとき。

個々のAPL実行環境は、不定期にティックハンドラーをチェックして実行できます。APL実行環境は1秒間に60回以上ティックハンドラーをチェックする必要がありますが、リソースに制約のある環境ではチェック頻度を少なくすることもできます。

たとえば、ティックハンドラーのminimumDelayを30に設定すると、APLは最多で30ミリ秒ごとにティックハンドラーを呼び出します。頻度はこれより低くなる場合があります。ティックカウンターを経過時間の測定に使わないでください。代わりに、elapsedTimeプロパティ、utcTimeプロパティのいずれかを使用します。

commands

このティックハンドラーが実行されたときに実行するコマンドの配列です。

生成されるイベントの形式は次のようになります。

"event": {
  "source": {
    "type": "COMPONENT_TYPE",   // コンポーネントの種類または"ドキュメント"
    "handler": "Tick",
    "id": ID,          // コンポーネントIDまたはnull(ドキュメントの場合)
    "uid": UID,        // ランタイムで生成されたコンポーネントの一意IDまたはnullドキュメントの場合)
    "value": NUMBER    // コンポーネントまたはnullドキュメントの場合)
  }
}

ティックコマンドは常に高速モードで実行されます。

minimumDelay

このティックハンドラーを次に呼び出すまでに経過する必要のある最小時間です。

when

trueの場合、システムがティックイベントを検出すると、このハンドラーが実行されます。

ティックハンドラーの例

アニメーションの例

ティックハンドラーを使用して、コマンドを定期的に実行するステートマシンを作成できます。以下は、ランダムな間隔(ただし、およそ4秒に1回)で信号機のライトを切り替えます。

{
  "type": "Container",
  "bind": [
    {
      "name": "CurrentIndex",
      "value": 0
    }
  ],
  "handleTick": [
    {
      "minimumDelay": 1000,
      "description": "信号機のライトをランダムな間隔で切り替えます。 切り替える順序は青、黄色、赤、緑色...です",
      "commands": [
        {
          "when": "${Math.random() < 0.25}",
          "type": "SetValue",
          "property": "CurrentIndex",
          "value": "${(CurrentIndex + 2) % 3}"
        }
      ]
    },
    {
      "minimumDelay": 15000,
      "description": "15秒ごとに赤に戻って人々を苛立たせます",
      "commands": [
        {
          "type": "SetValue",
          "property": "CurrentIndex",
          "value": 0
        }
      ]
    }
  ],
  "items": {
    "type": "Frame",
    "width": 100,
    "height": 100,
    "borderRadius": 100,
    "backgroundColor": "${index == CurrentIndex ? data : 'grey'}"
  },
  "data": [
    "red",
    "yellow",
    "green"
  ]
}

レート制限

ティックハンドラーを使用してレート制限を追加できます。たとえば、ユーザーが画面をスクロールする情報を定期的にスキルに渡す必要があるとします。

ティックハンドラーがない場合、SendEventコマンドをScrollViewonScrollハンドラーに割り当てることができます。このアプローチでは、コマンドがスクロールして表示内容が変わるたびにシステムに大量のSendEventメッセージを送信するため、問題が生じます。

{
  "type": "ScrollView",
  "onScroll": {
    "type": "SendEvent",
    "sequencer": "SendEventSequencer",
    "arguments": [
      "今の位置:${event.source.position}"
    ]
  }
}

これを改善するには、handleTickハンドラーを使ってメッセージを抑制します。この例では、ティックハンドラーが更新情報を送信する頻度を1秒あたり1回に制限し、最後に報告した位置が変更された場合に更新したスクロール位置を送信します。

{
  "type": "ScrollView",
  "bind": [
    {
      "name": "ScrollPosition",
      "value": 0
    },
    {
      "name": "LastScrollPosition",
      "value": 0
    }
  ],
  "handleTick": {
    "minimumDelay": 1000,
    "when": "${ScrollPosition != LastScrollPosition}",
    "commands": [
      {
        "type": "SetValue",
        "property": "LastScrollPosition",
        "value": "${ScrollPosition}"
      },
      {
        "type": "SendEvent",
        "sequencer": "SendEventSequencer",
        "arguments": [
          "今の位置:${ScrollPosition}"
        ]
      }
    ],
    "onScroll": {
      "type": "SetValue",
      "property": "ScrollPosition",
      "value": "{event.source.position}"
    }
  }
}