<template>
  <div class="score-container">
    <div class="score-description">
      <slot>最高数字</slot>
    </div>
    <div class="score-text-outer">
      <div ref="scoreDiv" class="score-text">{{ displayedScore }}</div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.score-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 120px;
  height: 55px;
  font-weight: bold;
  align-items: center;
  padding: 0 25px;
  margin-left: 10px;
  border-radius: 4px;
  background-color: rgb(187, 173, 160);
}

.score-description {
  font-size: 14px;
  color: rgb(238, 228, 218);
  user-select: none;
}

.score-text-outer {
  font-size: 25px;
  color: white;
  overflow: hidden;
}
</style>
<script lang="ts" setup>
import { AsyncQueue } from '@/utils/async-queue';
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';

const props = defineProps<{
  score: number;
}>();
const displayedScore = ref(props.score);
const scoreDiv = ref<HTMLDivElement>();
const scoreQueue = new AsyncQueue<number>();
let isUnmounted = false;
watch(
  () => props.score,
  (score) => {
    scoreQueue.push(score);
  },
);
onMounted(async () => {
  let continuouslyRolling = false;
  while (!isUnmounted) {
    try {
      const score = await scoreQueue.shift();
      await scoreDiv.value?.animate([{ transform: 'none' }, { transform: 'translateY(-100%)' }], {
        easing: continuouslyRolling ? 'linear' : 'ease-in',
        fill: 'forwards',
        duration: 100 / (scoreQueue.size + 1 + Number(continuouslyRolling)),
      }).finished;
      displayedScore.value = score;
      await nextTick();
      continuouslyRolling = scoreQueue.size > 0;
      await scoreDiv.value?.animate([{ transform: 'translateY(100%)' }, { transform: 'none' }], {
        easing: continuouslyRolling ? 'linear' : 'ease-out',
        fill: 'forwards',
        duration: 100 / (scoreQueue.size + 1),
      }).finished;
    } catch (e) {
      if (
        e instanceof AsyncQueue.CancelledError || //取消获取分数
        e instanceof DOMException //动画中途取消
      ) {
        return;
      }
    }
  }
});
onBeforeUnmount(() => {
  isUnmounted = true;
  scoreQueue.cancelGetters();
  scoreDiv.value?.getAnimations().forEach((animation) => animation.cancel());
});
</script>
