轮播图数据不够?别慌!前端 “循环群演” 大法来救场 🎬

0 阅读5分钟

引言:产品经理的 “无理要求”🤯

那天下午,产品经理突然拍了拍我的肩膀:“小老弟,咱们这个指标轮播图,每一页都必须放满 4 个,哪怕数据不够也不能空着!空着多难看,影响用户体验!”

我低头看了看接口返回的数据:10 个指标。10 除以 4 等于 2.5,这意味着最后一页只有 2 个指标,剩下两个位置空着,像极了我掉了两颗牙的嘴巴 —— 要多尴尬有多尴尬。

但作为一个专业的前端,我怎么能说 “不行” 呢?我微微一笑:“没问题,包在我身上!”(内心 OS:还好我有摸鱼小技巧💡)


问题场景:轮播图的 “空窗期”😱

先看看我们的原始数据:

const rawItems = [
  { title: "指标 A", blocks: ["a", "b", "c"] },
  { title: "指标 B", blocks: ["a", "b", "c", "d"] },
  { title: "指标 C", blocks: ["a", "b"] },
  { title: "指标 D", blocks: ["a", "b", "c", "d", "e"] },
  { title: "指标 E", blocks: ["a"] },
  { title: "指标 F", blocks: ["a", "b", "c"] },
  { title: "指标 G", blocks: ["a", "b", "c", "d"] },
  { title: "指标 H", blocks: ["a", "b"] },
  { title: "指标 I", blocks: ["a", "b", "c", "d", "e"] },
  { title: "指标 J", blocks: ["a"] },
];

10 个指标,每页放 4 个,正常情况下:

  • 第 1 页:A、B、C、D(完美)
  • 第 2 页:E、F、G、H(完美)
  • 第 3 页:I、J、?、?(空了两个位置!社死现场)

这时候如果直接渲染,最后一页就会像这样:

┌─────────────┬─────────────┬─────────────┬─────────────┐
│   指标 I     │   指标 J    │             │             │
│  ● ● ● ● ●  │  ●          │             │             │
└─────────────┴─────────────┴─────────────┴─────────────┘

产品经理看到肯定会说:“这不行!太丑了!”


解决方案:“循环群演” 大法✨

既然数据不够,那我们就把前面的指标抓过来当 “群演” !反正用户在轮播的时候,也不会太注意是不是重复的,只要页面满满当当,视觉效果好就行!

核心思路:

  • 先按每页 4 个把数据切好

  • 检查最后一页够不够 4 个

  • 如果不够,就从第 1 个指标开始循环,补够 4 个

这里的关键是取模运算(%) ,它能让我们像循环播放背景音乐一样,循环利用前面的数据!


代码解析:手把手教你 “抓群演”👨‍💻

直接上核心代码:

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>TDesign Swiper Demo</title>

    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

    <link
      rel="stylesheet"
      href="https://unpkg.com/tdesign-vue-next/dist/tdesign.min.css"
    />
    <script src="https://unpkg.com/tdesign-vue-next/dist/tdesign.min.js"></script>

    <style>
      /* 自定义样式以改善整体外观 */
      body {
        margin: 24px;
        background-color: #f6f6f6;
      }

      #app {
        max-width: 1200px;
        margin: 0 auto;
        padding: 16px;
      }

      .indicator-item {
        background-color: #f6f6f6;
        border-radius: 8px;
        padding: 16px;
        text-align: center;
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }

      .indicator-title {
        font-size: 16px;
        font-weight: 600;
        color: #333;
        margin-bottom: 8px;
      }

      .indicator-blocks {
        display: flex;
        gap: 4px;
        flex-wrap: wrap;
        justify-content: center;
      }

      .indicator-block {
        width: 8px;
        height: 8px;
        background-color: #0052d9;
        border-radius: 50%;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <t-card title="示例轮播" style="height: 300px; padding: 16px">
        <t-swiper
          :autoplay="true"
          :interval="3000"
          :loop="true"
          :navigation="{type:'fraction'}"
        >
          <t-swiper-item v-for="(slide, sIdx) in slides" :key="sIdx">
            <t-row :gutter="[16, 16]">
              <t-col
                v-for="(item, idx) in slide"
                :key="idx"
                :xs="12"
                :sm="6"
                :md="6"
                :lg="3"
                :xl="3"
              >
                <div class="indicator-item">
                  <div class="indicator-title">{{ item.title }}</div>
                  <div class="indicator-blocks">
                    <div
                      v-for="(block, i) in item.blocks || []"
                      :key="i"
                      class="indicator-block"
                    ></div>

                    <div
                      v-if="!item.blocks || item.blocks.length === 0"
                      class="indicator-block"
                      v-for="i in 3"
                      :key="'default'+i"
                    ></div>
                  </div>
                </div>
              </t-col>
            </t-row>
          </t-swiper-item>
        </t-swiper>
      </t-card>
    </div>

    <script>
      const { createApp, computed } = Vue;

      const rawItems = [
        { title: "指标 A", blocks: ["a", "b", "c"] },
        { title: "指标 B", blocks: ["a", "b", "c", "d"] },
        { title: "指标 C", blocks: ["a", "b"] },
        { title: "指标 D", blocks: ["a", "b", "c", "d", "e"] },
        { title: "指标 E", blocks: ["a"] },
        { title: "指标 F", blocks: ["a", "b", "c"] },
        { title: "指标 G", blocks: ["a", "b", "c", "d"] },
        { title: "指标 H", blocks: ["a", "b"] },
        { title: "指标 I", blocks: ["a", "b", "c", "d", "e"] },
        { title: "指标 J", blocks: ["a"] },
      ];

      // Vue应用对象定义
      const App = {
        setup() {
          // 每个幻灯片中显示的项目数量
          const itemsPerSlide = 4;

          const slides = computed(() => {
            // 获取原始数据的长度
            const n = rawItems.length;
            // 如果原始数据为空,则直接返回空数组
            if (n === 0) return [];

            // 初始化结果数组
            const result = [];

            // 按照每页itemsPerSlide个项目进行循环处理
            // i是每次循环的起始索引,每次递增itemsPerSlide
            for (let i = 0; i < n; i += itemsPerSlide) {
              // 从rawItems中截取从索引i开始,到i+itemsPerSlide结束的片段
              const chunk = rawItems.slice(i, i + itemsPerSlide);

              // 检查当前块的长度是否小于每页应该显示的数量
              if (chunk.length < itemsPerSlide) {
                // 计算还需要多少个项目才能达到每页所需数量
                const need = itemsPerSlide - chunk.length;

                // 循环添加前几个项目来填充剩余位置
                for (let k = 0; k < need; k++) {
                  // 使用取模运算符来循环利用前面的项目
                  // 当k超过原始数组长度时,使用k%n来重新从头开始
                  chunk.push(rawItems[k % n]);
                }
              }

              // 将当前块添加到结果数组中
              result.push(chunk);
            }

            return result;
          });

          return {
            slides,
          };
        },
      };

      createApp(App).use(TDesign).mount("#app");
    </script>
  </body>
</html>

灵魂拷问:k % n 是啥?🤔

这就是我们的 “循环播放键”!

  • k=0 时,0 % 10 = 0,抓第 1 个指标(A)
  • k=1 时,1 % 10 = 1,抓第 2 个指标(B)
  • k=10 时,10 % 10 = 0,又回到第 1 个指标(A)

就像这样:

原始数据:[A, B, C, D, E, F, G, H, I, J]
最后一页:[I, J]
需要补2个
抓来的群演:A (0%10), B (1%10)
补齐后:[I, J, A, B]

完美!最后一页变成了:

┌─────────────┬─────────────┬─────────────┬─────────────┐
│   指标 I    │   指标 J     │   指标 A     │   指标 B    │
│  ● ● ● ● ●  │  ●          │  ● ● ●      │  ● ● ● ●    │
└─────────────┴─────────────┴─────────────┴─────────────┘

满满当当,产品经理看了都笑开了花🌸!


效果展示:轮播图的 “完美形态”🎨

2026-03-1614.57.40-ezgif.com-video-to-gif-converter.gif

现在我们的轮播图:

  • 第 1 页:A、B、C、D
  • 第 2 页:E、F、G、H
  • 第 3 页:I、J、A、B(群演上线!)

用户在轮播的时候,只会觉得 “哇,这个轮播图内容好丰富,每一页都满满的”,根本不会注意到 A 和 B 又出现了一次!

而且我们用了 TDesign 的Swiper组件,配合autoplayloop,体验丝滑得像德芙巧克力🍫!


总结:摸鱼小技巧,你学会了吗?😏

这个 “循环群演” 大法:

  • 简单实用:几行代码就能解决问题

  • 视觉效果好:页面满满当当,不再空落落

  • 用户体验佳:轮播起来丝滑流畅

  • 方便摸鱼:解决了产品经理的需求,你又能多摸 5 分钟鱼了!

以后再遇到数据不够的情况,别慌,试试这个 “循环群演” 大法,让你的前端页面永远 “满满当当”!


最后互动:你们平时遇到数据不够的情况,都是怎么处理的?欢迎在评论区分享你的摸鱼小技巧!👇

如果觉得这篇文章有用,别忘了点赞👍、收藏⭐、转发📤,让更多的前端小伙伴一起快乐摸鱼!