<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
import DesignStage from "../../../components/DesignStage.vue";
import type Player from "@/commons/player.ts";
import { $is_local_mode } from "@/commons/utils.ts";
import { cssAssetUrl, assetUrl } from "@/commons/assets.ts";
import { playApplauseMusic, stopApplauseMusic } from "@/commons/music";
const defaultAvatarUrl = assetUrl("game4/default-avator.webp");

const props = defineProps<{
  rankList: Player[];
  /** H5 玩家操控产生的加速度: { [userid]: boost(叠加系数, 0~N) } */
  horseBoosts: Record<string, number>;
}>();

const emit = defineEmits<{
  start: [];
  next: [];
  back: [];
}>();

const designStage = ref<InstanceType<typeof DesignStage> | null>(null);

const imageUrls = {
  bgTop: cssAssetUrl("game4/bg-top.webp"),
  bgBottom: cssAssetUrl("game4/bg-bottom.webp"),
  logo: cssAssetUrl("game1/logo.png"),
  titleBg: cssAssetUrl("game4/horse-title-bg.webp"),
  bigBrum: cssAssetUrl("game4/big-brum.webp"),
  username: cssAssetUrl("game4/username.svg"),
  avatar: cssAssetUrl("game4/default-avator.webp"),
  resultBg: cssAssetUrl("game4/resultbg.webp"),
  next: cssAssetUrl("game1/next.png"),
  back: cssAssetUrl("game1/back.png"),
  // 结果弹窗 - podium 相关资源（复用 game1）
  podium: cssAssetUrl("game4/podium.webp"),
  fireworks: cssAssetUrl("game1/fireworks.png"),
  guan1: cssAssetUrl("game1/guan1.png"),
  pai1: cssAssetUrl("game4/pai1.webp"),
  guan2: cssAssetUrl("game1/guan2.png"),
  pai2: cssAssetUrl("game4/pai2.webp"),
  guan3: cssAssetUrl("game1/guan3.png"),
  pai3: cssAssetUrl("game4/pai3.webp"),
  rankIcon: cssAssetUrl("game1/no-icon.png"),
  avatarAnimation: cssAssetUrl("avatar_animation.png"),
};

// ========== 速度基准 ==========
// 60s 游戏 × 基准速度(0.455%/s) = 27.3% → 不加速跑约 1/4 路程
// 60s 游戏 × 最大3.5x(1.59%/s) = 95.5% → 猛摇才能接近终点
const TOTAL_DISTANCE_PX = 2160; // -120px ~ 2040px
const BASE_DURATION_SEC = 220;
const BASE_SPEED_PERCENT_PER_SEC = 100 / BASE_DURATION_SEC; // ≈ 0.455%/s 基准速度

// 马匹精灵图帧动画（horse.webp 共 12 帧，改用 JS 切帧，避免 CSS steps 在部分浏览器不生效）
const HORSE_FRAME_COUNT = 12;
const HORSE_FRAME_TICK_MS = 48; // ~20fps
const HORSE_SPRITE_OFFSET_X = -17;
const HORSE_SPRITE_FRAME_WIDTH = 120;
const horseSpriteUrl = assetUrl("game4/horse.webp");

// ========== 瞬时加速脉冲机制 ==========
// H5 每次摇一摇发送累计积分，PC 端检测 score 增长 → 触发短暂加速脉冲 → 迅速衰减
// 快速连摇时脉冲可叠加（每次 +1.0），最高不超过 4.5（即 5.5x 基准速度）
const BOOST_PULSE = 1.0; // 每次操作的加速幅度（+100% 额外速度，即 2x 基准）
const MAX_PULSE = 4.5; // 最大叠加脉冲上限（即 5.5x 基准速度）
const BOOST_DECAY = 0.992; // 每帧衰减系数（60fps 下约 3.6s 衰减到 60%，约 9s 衰减归零）

/** 瞬时加速值: { [userid]: currentBoost }，每帧衰减 */
const transientBoosts = ref<Record<string, number>>({});
/** 上一轮各玩家累计积分，用于检测增长 */
const prevScores: Record<string, number> = {};

/** 检测 rankList 中哪些玩家 score 增长了 → 触发瞬时加速脉冲 */
const detectScoreChanges = (players: Player[]) => {
  if (!players || players.length === 0) return;

  let hasNewBoost = false;
  const updated = { ...transientBoosts.value };

  players.forEach((p, index) => {
    // WebSocket 数据无 userid/wechat_id，用 avatar 作为稳定标识
    const id = p.userid || p.wechat_id || p.avatar || `player-${index}`;
    if (!id || id.startsWith("empty-")) return;
    const newScore = Number(p.score ?? 0);
    const prev = prevScores[id] ?? 0;

    if (newScore > prev) {
      // 积分增长 = 玩家又摇了一次 → 叠加瞬时加速脉冲（连摇可累积到 MAX_PULSE）
      const current = updated[id] || 0;
      updated[id] = Math.min(current + BOOST_PULSE, MAX_PULSE);
      hasNewBoost = true;
      console.log(
        `[Rank1View] 🚀 ${p.nickname} score ${prev}→${newScore}, boost: ${current.toFixed(1)}+${BOOST_PULSE}=${updated[id].toFixed(1)}`,
      );
    }

    prevScores[id] = newScore;
  });

  if (hasNewBoost) {
    transientBoosts.value = updated;
  }
};

interface Horse {
  id: string;
  lane: number;
  startY: number;
  xPercent: number;
  boost: number; // boost=0 按基准60秒跑完，boost越大越快
  nickname: string;
  avatar: string;
  frameIndex: number;
  frameTimer: number;
}

interface RankItem {
  rank: number;
  userid: string;
  nickname: string;
  avatar: string;
  score: number;
}

const horses = ref<Horse[]>([]);
const isGameRunning = ref(false);
const showResult = ref(false);
const finalRankList = ref<RankItem[]>([]);
/** 补齐占位的完整 10 人排名列表 */
const fullRankList = computed<RankItem[]>(() => {
  const result: RankItem[] = [];
  for (let i = 0; i < 10; i++) {
    result.push(
      finalRankList.value[i] ?? {
        rank: i + 1,
        userid: `empty-${i}`,
        nickname: "虚位以待",
        avatar: "",
        score: 0,
      },
    );
  }
  return result;
});
/** 倒计时结束后才为 true，控制背景动画和马匹渲染 */
const gameStarted = ref(false);

const LANE_POSITIONS = [42, 160, 270, 380, 490];

const updateHorses = (players: Player[]) => {
  const validPlayers = players.filter(
    (p) => p.nickname !== "虚位以待" && !p.wechat_id?.startsWith("empty-"),
  );

  if (validPlayers.length === 0) return;

  // 保留已存在的 xPercent 和 boost，避免新玩家列表覆盖进度
  const existingMap = new Map(horses.value.map((h) => [h.id, h]));

  horses.value = validPlayers.map((player, index) => {
    // WebSocket 数据无 userid/wechat_id，用 avatar 作为稳定标识
    const id = player.userid || player.wechat_id || player.avatar || `horse-${index}`;
    const existing = existingMap.get(id);
    return {
      id,
      lane: index % LANE_POSITIONS.length,
      startY: LANE_POSITIONS[index % LANE_POSITIONS.length]!,
      xPercent: existing?.xPercent ?? 0,
      boost: existing?.boost ?? 0,
      nickname: player.nickname,
      avatar: player.avatar || "",
      frameIndex: existing?.frameIndex ?? Math.floor(Math.random() * HORSE_FRAME_COUNT),
      frameTimer: existing?.frameTimer ?? 0,
    };
  });
};

const mockAvatars = [
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Felix',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Aneka',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Salem',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Midnight',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Lucky',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Bandit',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Misty',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Pepper',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Oreo',
  'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Tiger',
];
const mockNicknames = ['疾风骑士', '闪电侠', '追风者', '千里马', '飞毛腿', '烈焰驹', '暴风战士', '神行者', '银鞍客', '踏雪无痕'];

const mockHorses = () => {
  const mockPlayers: Player[] = Array.from({ length: 10 }, (_, i) => ({
    userid: `mock-${i}`,
    nickname: mockNicknames[i] || `玩家${i + 1}`,
    avatar: mockAvatars[i] || '',
    score: Math.floor(Math.random() * 60) + 10,
  }));
  updateHorses(mockPlayers);
};

watch(
  () => props.rankList,
  (players) => {
    if (players && players.length > 0) {
      updateHorses(players);
      // 检测积分增长 → 触发瞬时加速脉冲
      detectScoreChanges(players);
    }
  },
  { immediate: true, deep: true },
);

// 进度百分比 → 屏幕像素位置
const horseTranslateX = (xPercent: number) => {
  return -120 + (xPercent / 100) * TOTAL_DISTANCE_PX;
};

// ========== 接收 H5 传来的 acceleration / boost 数据 ==========
// 注意：rankList 的 score 增长检测是主通道，此处作为兜底
watch(
  () => props.horseBoosts,
  (boosts) => {
    if (!boosts || Object.keys(boosts).length === 0) return;

    let hasNew = false;
    const updated = { ...transientBoosts.value };
    Object.keys(boosts).forEach((id) => {
      const val = Number(boosts[id] ?? 0);
      if (val > 0) {
        // 每次收到有效数据都叠加脉冲（连摇可累积到 MAX_PULSE）
        const current = updated[id] || 0;
        updated[id] = Math.min(current + BOOST_PULSE, MAX_PULSE);
        hasNew = true;
      }
    });

    if (hasNew) {
      transientBoosts.value = updated;
      console.log(
        `[Rank1View] horseBoosts 触发瞬时加速 (${new Date().toLocaleTimeString()}):`,
        JSON.parse(JSON.stringify(transientBoosts.value)),
      );
    }
  },
  { deep: true },
);

// ========== 游戏主循环 (requestAnimationFrame 驱动位置 + 帧动画) ==========
let gameRafId: number | undefined;
let gameLastTs = 0;

const startGameLoop = () => {
  stopGameLoop();
  gameLastTs = 0;

  const tick = (ts: number) => {
    if (!isGameRunning.value) {
      gameRafId = requestAnimationFrame(tick);
      return;
    }
    if (!gameLastTs) gameLastTs = ts;
    const dt = Math.min(ts - gameLastTs, 200);
    gameLastTs = ts;

    const newTransientBoosts: Record<string, number> = {};

    horses.value.forEach((horse) => {
      if (horse.xPercent >= 100) return;

      const boost = transientBoosts.value[horse.id] || 0;
      horse.boost = boost;

      horse.xPercent = Math.min(
        100,
        horse.xPercent +
          BASE_SPEED_PERCENT_PER_SEC * (1 + boost) * (dt / 1000),
      );

      horse.frameTimer += dt;
      while (horse.frameTimer >= HORSE_FRAME_TICK_MS) {
        horse.frameTimer -= HORSE_FRAME_TICK_MS;
        horse.frameIndex = (horse.frameIndex + 1) % HORSE_FRAME_COUNT;
      }

      const decayed = boost * Math.pow(BOOST_DECAY, dt / (1000 / 60));
      if (decayed > 0.005) {
        newTransientBoosts[horse.id] = decayed;
      }
    });

    transientBoosts.value = newTransientBoosts;
    gameRafId = requestAnimationFrame(tick);
  };

  gameRafId = requestAnimationFrame(tick);
};

const stopGameLoop = () => {
  if (gameRafId) {
    cancelAnimationFrame(gameRafId);
    gameRafId = undefined;
  }
};

// ========== 本地 mock 加速 ==========
let mockBoostTimer: ReturnType<typeof setInterval> | undefined;
const startMockBoost = () => {
  stopMockBoost();
  // 基于马匹顺序计算速度系数：排名靠前的马（小索引）基础快，末尾的慢
  const horseRates: Record<string, number> = {};
  horses.value.forEach((h, i) => {
    horseRates[h.id] = 0.3 + (i / Math.max(horses.value.length - 1, 1)) * 0.7;
  });

  mockBoostTimer = setInterval(() => {
    if (!isGameRunning.value) return;
    const updated = { ...transientBoosts.value };
    horses.value.forEach((horse) => {
      const rate = horseRates[horse.id] ?? (0.3 + Math.random() * 0.7);
      const current = updated[horse.id] || 0;
      // 偶尔减速制造节奏变化
      const decay = Math.random() > 0.75 ? -0.4 : 0;
      updated[horse.id] = Math.max(0, Math.min(current + BOOST_PULSE * rate * 0.3 + decay, MAX_PULSE));
    });
    transientBoosts.value = updated;
  }, 1500);
};
const stopMockBoost = () => {
  if (mockBoostTimer) {
    clearInterval(mockBoostTimer);
    mockBoostTimer = undefined;
  }
};

const computeFinalRank = (): RankItem[] => {
  // 从 WebSocket rankList 中取有效玩家（不排序，直接使用 WebSocket 返回的顺序）
  const valid = (props.rankList || []).filter(
    (p) => p.nickname !== "虚位以待" && !(p.wechat_id || "").startsWith("empty-"),
  );

  if (valid.length > 0) {
    // 直接使用 WebSocket 返回的顺序，不进行排序
    return valid.map((p, i) => ({
      rank: i + 1,
      userid: p.userid || p.wechat_id || `unknown-${i}`,
      nickname: p.nickname,
      avatar: p.avatar || "",
      score: Number(p.score ?? 0),
    }));
  }

  // 无 WebSocket 数据时降级：按 xPercent 降序（本地模拟模式）
  return horses.value
    .slice()
    .sort((a, b) => b.xPercent - a.xPercent)
    .map((h, i) => ({
      rank: i + 1,
      userid: h.id,
      nickname: h.nickname,
      avatar: h.avatar,
      score: Math.round(h.xPercent * 10),
    }));
};

const stopGame = () => {
  isGameRunning.value = false;
  showResult.value = true;
  stopGameLoop();
  stopMockBoost();

  // 播放喝彩音效
  playApplauseMusic();

  // 计算最终排名
  finalRankList.value = computeFinalRank();

  // 打印排名数据到控制台
  console.log("========== 游戏结束 - 最终排名 ==========");
  console.table(
    finalRankList.value.map((item) => ({
      名次: item.rank,
      用户ID: item.userid,
      昵称: item.nickname,
      分数: item.score,
    })),
  );
  console.log("原始排名数据:", JSON.parse(JSON.stringify(finalRankList.value)));
};

const startIntro = async () => {
  if ($is_local_mode()) {
    mockHorses();
  }
  showResult.value = false;
  finalRankList.value = [];
  gameStarted.value = false;
  isGameRunning.value = false;
  stopApplauseMusic();
  transientBoosts.value = {};
  Object.keys(prevScores).forEach((k) => delete prevScores[k]);

  const countdownPromise = designStage.value?.startCountdown(3, 60);
  await new Promise((r) => setTimeout(r, 3500));

  gameStarted.value = true;
  isGameRunning.value = true;
  
  if (horses.value.length === 0) {
    const count = 8 + Math.floor(Math.random() * 3);
    const mockPlayers: Player[] = Array.from({ length: count }, (_, i) => ({
      userid: `auto-${i}`,
      nickname: mockNicknames[i] || `马匹${i + 1}`,
      avatar: mockAvatars[i] || '',
      score: Math.floor(Math.random() * 60) + 10,
    }));
    updateHorses(mockPlayers);
    console.log('[game4 Rank1View] 生成', count, '匹默认马');
  }
  
  startGameLoop();
  if ($is_local_mode()) {
    startMockBoost();
  }

  await countdownPromise;
  stopGame();
};

const horseAvatarStyle = (horse: Horse) => {
  if (!horse.avatar) return {};
  return { backgroundImage: `url(${horse.avatar})`, backgroundSize: "cover" };
};

const horseSpriteStyle = (horse: Horse) => ({
  backgroundImage: `url(${horseSpriteUrl})`,
  backgroundPosition: `${HORSE_SPRITE_OFFSET_X - horse.frameIndex * HORSE_SPRITE_FRAME_WIDTH}px 0`,
  backgroundSize: "1472px 109px",
});

const preloadHorseSprite = () => {
  const img = new Image();
  img.src = horseSpriteUrl;
};

// 结果弹窗头像渲染（参考 game1 Rank2View）
const renderAvatar = (item: any) => {
  if (item && item.avatar) {
    return `background: url('${item.avatar}') center / cover no-repeat;`;
  }
  // 无头像时用 default-avator 占位（白底 + 居中 cover）
  return `background: #fff url('${defaultAvatarUrl}') center / cover no-repeat;`;
};

// 获取 podium 上三名选手（不足补空）
const podiumPlayers = (): RankItem[] => {
  const result = fullRankList.value.slice(0, 3);
  // 按 podium 视觉顺序：2 1 3（第二名在左，第三名在右）
  return [result[1]!, result[0]!, result[2]!];
};

onMounted(() => {
  preloadHorseSprite();
  // 本地模式：Game4.vue onMounted 会调用 startIntro() 来控制完整流程
  // 此处不自动开始，避免与 startIntro 冲突
});

onUnmounted(() => {
  // 仅清理游戏资源，不操作音乐（音乐由 Game4.vue 统一管理）
  // 注意：不能调 stopApplauseMusic()，因为 backgroundMusic 是全局共享的，
  // 组件卸载时 Game4.vue 的 watch 可能已将其切换为 BGM，pause 会误伤 BGM
  isGameRunning.value = false;
  stopGameLoop();
  stopMockBoost();
});

defineExpose({
  startIntro,
});
</script>

<template>
  <DesignStage ref="designStage">
    <div class="screen-container" :class="{ paused: !gameStarted || showResult }">
      <div class="img-logo"></div>
      <div class="img-title-bg"></div>
      <div class="img-big-brum"></div>
      <div class="bg-top">
        <div class="bg-top-inner"></div>
        <div class="bg-top-inner"></div>
      </div>
      <div class="bg-bottom">
        <div class="bg-bottom-inner"></div>
        <div class="bg-bottom-inner"></div>
      </div>
      <!-- 倒计时结束后才显示马匹 -->
      <template v-if="gameStarted">
        <div
          v-for="horse in horses"
          :key="horse.id"
          class="horse-wrapper"
          :style="{
            bottom: `${horse.startY}px`,
            transform: `translateX(${horseTranslateX(horse.xPercent)}px)`,
          }"
        >
          <div class="horse" :style="horseSpriteStyle(horse)">
            <div class="horse-shadow"></div>
          </div>
          <div class="horse-username">
            <span class="username-text">{{ horse.nickname }}</span>
          </div>
          <div class="horse-avatar" :style="horseAvatarStyle(horse)"></div>
        </div>
      </template>
      <div v-if="showResult" class="result-overlay">
        <div class="result-mask"></div>

        <!-- 领奖台 podium（参考 game1 Rank2View） -->
        <div class="podium">
          <div
            v-for="(item, idx) in podiumPlayers()"
            :key="item.userid || `podium-${idx}`"
            :class="['rank-no', `rank-no${idx === 1 ? 1 : idx === 0 ? 3 : 2}`]"
          >
            <div class="gu-burst"></div>
            <div class="avatar" :style="renderAvatar(item)"></div>
            <div class="nickname">{{ item.nickname }}</div>
            <div class="guan"></div>
            <!-- <div class="pai"></div> -->
          </div>
        </div>

        <!-- 第4-10名 rank-container -->
        <div class="rank-container">
          <div
            v-for="(item, index) in fullRankList.slice(3, 10)"
            :key="item.userid || `rank-${index}`"
          >
            <div class="no-icon">{{ index + 4 }}</div>
            <div>
              <div class="img-avatar-container">
                <div class="img-avatar" :style="renderAvatar(item)"></div>
              </div>
              <div class="txt-nickname">{{ item.nickname }}</div>
            </div>
          </div>
        </div>

        <div class="btn-next" @click="emit('next')"></div>
        <div class="btn-back" @click="emit('back')"></div>
      </div>
    </div>
  </DesignStage>
</template>

<style scoped>
.screen-container {
  width: 1920px;
  height: 1080px;
  position: relative;
  overflow: hidden;
}

.img-logo {
  background: v-bind("imageUrls.logo");
  background-size: cover;
  width: 428px;
  height: 77px;
  position: absolute;
  left: 37px;
  top: 82px;
  z-index: 20;
}

.img-title-bg {
  background: v-bind("imageUrls.titleBg");
  background-size: cover;
  width: 743px;
  height: 164.2px;
  position: absolute;
  left: 50%;
  top: 16px;
  transform: translateX(-50%);
  z-index: 20;
}

.img-big-brum {
  background: v-bind("imageUrls.bigBrum");
  background-size: cover;
  width: 161px;
  height: 150px;
  position: absolute;
  right: 59px;
  top: 392px;
  z-index: 2;
}

.screen-container.paused .bg-top,
.screen-container.paused .bg-bottom,
.screen-container.paused .horse {
  animation-play-state: paused;
}

.bg-top {
  width: 7680px;
  height: 1080px;
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  animation: bg-scroll-left 40s linear infinite;
  z-index: 1;
}

.bg-top-inner {
  width: 3840px;
  height: 1080px;
  background: v-bind("imageUrls.bgTop") 0 0 / 3840px 1080px
    no-repeat;
  flex-shrink: 0;
}

.bg-bottom {
  width: 7680px;
  height: 577px;
  position: absolute;
  bottom: 0;
  left: 0;
  display: flex;
  animation: bg-scroll-left 40s linear infinite;
  z-index: 2;
}

.bg-bottom-inner {
  width: 3840px;
  height: 577px;
  background: v-bind("imageUrls.bgBottom") 0 0 / 3840px 577px
    no-repeat;
  flex-shrink: 0;
}

.horse-wrapper {
  position: absolute;
  left: 0;
  transition: transform 0.2s linear;
  will-change: transform;
  z-index: 10;
}

.horse {
  width: 120px;
  height: 109px;
  background-repeat: no-repeat;
  position: relative;
}

.horse-shadow {
  position: absolute;
  bottom: -2px;
  left: 50%;
  transform: translateX(-50%);
  width: 120px;
  height: 18px;
  background: radial-gradient(ellipse, rgba(0, 0, 0, 0.45) 0%, transparent 70%);
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
}

.horse-username {
  background: v-bind("imageUrls.username");
  background-size: contain;
  background-repeat: no-repeat;
  width: 100px;
  height: 40px;
  position: absolute;
  top: 10px;
  left: -22px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.username-text {
  position: absolute;
  top: 1px;
  color: #000;
  font-size: 14px;
  font-weight: bold;
  max-width: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-left: 4px;
  box-sizing: border-box;
}

.horse-avatar {
  background: v-bind("imageUrls.avatar");
  background-size: cover;
  width: 30px;
  height: 30px;
  position: absolute;
  top: -4px;
  left: -22px;
  border-radius: 50%;
}

@keyframes bg-scroll-left {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-3840px);
  }
}


/* ========== 结果弹窗 ========== */
.result-overlay {
  width: 1920px;
  height: 1080px;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.result-mask {
  width: 1920px;
  height: 1080px;
  position: absolute;
  top: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 0;
}

/* ========== podium 领奖台 ========== */
.podium {
  background: v-bind("imageUrls.podium") no-repeat center;
  width: 1347px;
  height: 412px;
  position: absolute;
  top: 294px;
  left: 287px;
  z-index: 1;
}

.gu-burst {
  width: 200px;
  height: 200px;
  position: absolute;
  z-index: 1;
  border-radius: 50%;
  opacity: 1;
  transform: scale(0.6);
  transform-origin: center center;
  background: v-bind("imageUrls.fireworks") center / cover no-repeat;
  pointer-events: none;
  animation: gu-burst-expand 1.2s ease-out infinite;
}

@keyframes gu-burst-expand {
  0% {
    opacity: 1;
    transform: scale(0.6);
  }
  100% {
    opacity: 0;
    transform: scale(1);
  }
}

.rank-no {
  position: relative;
}

/* 第1名 (中间) */
.rank-no1 {
  .gu-burst { left: 605px; top: -235px; }
  .avatar {
    width: 120px; height: 120px; border-radius: 60px;
    position: absolute; left: 610px; top: -141px;
  }
  .nickname {
    width: 167px; height: 46px;
    background: #FFCF44; color: white;
    text-align: center; line-height: 46px;
    position: absolute; left: 590px; top: -32px;
    z-index: 1; border-radius: 23px;
  }
  .guan {
    background: v-bind("imageUrls.guan1");
    width: 64px; height: 64px;
    position: absolute; top: -160px; left: 581px;
  }
  .pai {
    background: v-bind("imageUrls.pai1");
    width: 212px; height: 200px;
    position: absolute; left: 570px; top: 40px;
  }
}

/* 第2名 */
.rank-no2 {
  .gu-burst { left: 908px; top: -81px; }
  .avatar {
    width: 120px; height: 120px; border-radius: 60px;
    position: absolute; left: 920px; top: 25px;
  }
  .nickname {
    width: 167px; height: 46px;
    background: #CA9600; color: white;
    text-align: center; line-height: 46px;
    position: absolute; left: 897px; top: 136px;
    z-index: 1; border-radius: 23px;
  }
  .guan {
    background: v-bind("imageUrls.guan3") no-repeat center;
    width: 96px; height: 96px;
    position: absolute; left: 877px; top: -14px;
  }
  .pai {
    background: v-bind("imageUrls.pai3") no-repeat center;
    width: 212px; height: 200px;
    position: absolute; right: 130px; top: 50px;
  }
}

/* 第3名 */
.rank-no3 {
  .gu-burst { left: 318px; top: -120px; }
  .avatar {
    width: 120px; height: 120px; border-radius: 60px;
    position: absolute; left: 333px; top: -17px;
  }
  .nickname {
    width: 167px; height: 46px;
    background: #E2E2E2; color: #AFAFAF;
    text-align: center; line-height: 46px;
    position: absolute; top: 95px;left: 309px;
    z-index: 1; border-radius: 23px;
  }
  .guan {
    background: v-bind("imageUrls.guan2") no-repeat center;
    width: 96px; height: 96px;
    position: absolute; left: 289px; top: -54px;
  }
  .pai {
    background: v-bind("imageUrls.pai2") no-repeat center;
    width: 212px; height: 200px;
    position: absolute; left: 140px; top: 50px;
  }
}

/* ========== rank-container 第4-10名 ========== */
.rank-container {
  display: flex;
  align-items: center;
  width: 1270px;
  height: 115px;
  position: absolute;
  bottom: 243px;
  left: 325px;
  z-index: 1;
}

.rank-container > div {
  flex: 1;
  display: grid;
  width: 100px;
  height: 100%;
}

.rank-container .no-icon {
  background: v-bind("imageUrls.rankIcon") no-repeat center;
  width: 40px;
  height: 40px;
  position: absolute;
  text-align: center;
  line-height: 40px;
  color: white;
  font-weight: bold;
}

.rank-container .img-avatar-container {
  position: relative;
  margin: 0 auto;
  margin-top: 10px;
  width: 72px;
  height: 72px;
  border-radius: 36px;
}

.rank-container .img-avatar-container::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: v-bind("imageUrls.avatarAnimation");
  animation: avatar-rotate 10s linear infinite;
}

@keyframes avatar-rotate {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.rank-container .img-avatar {
  position: relative;
  top: 6px;
  z-index: 1;
  margin: 0 auto;
  width: 60px;
  height: 60px;
  border-radius: 30px;
  background: v-bind("imageUrls.avatar");
}

.rank-container .txt-nickname {
  color: white;
  font-size: 20px;
  text-align: center;
}

/* ========== btn-next ========== */
.btn-next {
  position: absolute;
  bottom: 50px;
  left: 620px;
  width: 316px;
  height: 105px;
  cursor: pointer;
  background: v-bind("imageUrls.next") center / cover no-repeat;
  z-index: 2;
}
.btn-back {
  position: absolute;
  bottom: 50px;
  left: 1020px;
  width: 316px;
  height: 105px;
  cursor: pointer;
  background: v-bind("imageUrls.back") center / cover no-repeat;
  z-index: 2;
}
</style>
