数据驱动与CSS预定义样式:实现灵活多变的Banner布局

背景

在开发一个活动Banner组件时,每个Banner需要展示不同数量的图片,且每张图片的位置、大小都需要精确控制,以创造出独特的视觉效果

传统的做法可能是:

  • 为每个Banner写独立的组件
  • 使用内联样式动态计算位置

但这些方案要么代码冗余,要么维护困难。我采用了一种更优雅的方案:数据驱动 + CSS预定义样式

核心思路

我们的方案包含两个关键部分:

  1. 数据配置层:通过配置数据定义每个Banner的图片数组和唯一标识
  2. 样式预定义层:在CSS中为每个可能的图片位置预定义样式类

通过动态生成类名,将数据和样式完美连接。

实现细节

1. 数据配置

首先,我们定义一个数据数组,每个Banner项包含:

  • id: 唯一标识符,用于生成CSS类名
  • img: 图片数组,支持不同数量的图片
  • 其他业务数据(数量、提示文字、链接等)
tsx 复制代码
const list = [
  {
    id: "four",
    num: 3,
    hint: t("exclusiveEventDesc4"),
    img: [img1, img2],  // 2张图片
    link: "https://forms.gle/XshRsPmkrKnaQUdA8",
    targetTime: '2025-11-15T13:00:00Z' 
  },
  {
    id: "two",
    num: 9,
    hint: t("exclusiveEventDesc2"),
    img: [img1, img2, img3, img4],  // 4张图片
    link: "https://forms.gle/UJ1oPxGgDzRqJ1y6A",
    targetTime: '2025-11-13T13:00:00Z'
  },
  {
    id: "one",
    num: 50,
    hint: t("exclusiveEventDesc1"),
    img: [img1, img2, img3, img4],  // 4张图片
    link: "https://tinyurl.com/2025CCCCBREAKFAST",
    targetTime: '2025-11-13T13:00:00Z'
  },
];

2. 动态类名生成

在渲染时,我们通过模板字符串动态生成类名:

tsx 复制代码
<div className="kol-img">
  {item.img?.map((citem, cindex) => (
    < img 
      src={citem} 
      key={cindex} 
      className={`${item.id}-img${cindex + 1}`}  // 关键:动态生成类名
    />
  ))}
</div>

类名生成规则

  • item.id = "one", cindex = 0 → 类名:one-img1
  • item.id = "two", cindex = 1 → 类名:two-img2
  • item.id = "four", cindex = 0 → 类名:four-img1

每个Banner的每张图片都对应唯一的类名。

3. CSS预定义样式

在CSS文件中,我们为每个可能的类名组合预定义样式:

less 复制代码
.kol-img {
  position: relative;
  img {
    filter: grayscale(100%);
    border-radius: 100px;
  }
}

// Banner "one" 的4张图片布局
.one-img1 {
  width: 126px;
  position: absolute;
  top: 130px;
  left: 20px;
  @media @SmallScreen {
    width: 44px;
    top: 10px;
  }
}

.one-img2 {
  width: 88px;
  position: absolute;
  left: 130px;
  top: 85px;
  @media @SmallScreen {
    width: 72px;
    left: 56px;
    top: 28px;
  }
}

.one-img3 {
  width: 72px;
  position: absolute;
  left: 39px;
  top: 49px;
  @media @SmallScreen {
    width: 76px;
    top: 96px;
    left: 0;
  }
}

.one-img4 {
  width: 72px;
  position: absolute;
  left: 146px;
  top: 231px;
  @media @SmallScreen {
    width: 44px;
    left: 70px;
    top: 170px;
  }
}

// Banner "two" 的4张图片布局
.two-img1 {
  width: 125px;
  position: absolute;
  top: 56px;
  left: 100px;
  @media @SmallScreen {
    width: 75px;
    top: 80px;
    left: 60px;
  }
}

.two-img2 {
  width: 90px;
  position: absolute;
  left: 13px;
  top: 160px;
  @media @SmallScreen {
    width: 43px;
    top: 44px;
    left: 28px;
  }
}

// ... 更多样式定义

4. 完整组件代码

tsx 复制代码
export default function BannerKOL() {
  const [t] = useTranslation();
  const isMobile = useIsMobile();

  const list = [
    // ... 数据配置
  ];

  return (
    isMobile ? (
      <div className="bannerkol-wrap">
        {list.map((item, index) => (
          <div key={index}>
            <div className="kol-line"></div>
            <div className="kol-box df">
              <div className="kol-img">
                {item.img?.map((citem, cindex) => (
                  < img 
                    src={citem} 
                    key={cindex} 
                    className={`${item.id}-img${cindex + 1}`} 
                  />
                ))}
              </div>
              <div className="kol-info">
                {/* 其他内容 */}
              </div>
            </div>
          </div>
        ))}
      </div>
    ) : (
      <Marquee className="bannerkol-marquee" speed={60} autoFill gradient={false} pauseOnHover={true}>
        <div className="bannerkol-wrap">
          {list.map((item, index) => (
            <div key={index}>
              <div className="kol-line"></div>
              <div className={`kol-box df ${item.img?.length ? '' : 'no-img'}`}>
                <div className="kol-img">
                  {item.img?.map((citem, cindex) => (
                    < img 
                      src={citem} 
                      key={cindex} 
                      className={`${item.id}-img${cindex + 1}`} 
                    />
                  ))}
                </div>
                <div className="kol-info">
                  {/* 其他内容 */}
                </div>
              </div>
            </div>
          ))}
        </div>
      </Marquee>
    )
  );
}

总结

通过数据驱动 + CSS预定义样式 的组合,我们实现了一个既灵活又易维护的Banner组件。这种方案的核心在于:数据驱动,易于扩展 ,数据和视图解耦, 添加新的Banner只需在list数组中添加配置,而且无需修改组件逻辑代码,配置与展示逻辑完全分离,产品提新的需求只需要改动数据,易于迭代

相关推荐
JIngJaneIL1 小时前
基于Java失物招领系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·vue
鼎道开发者联盟1 小时前
当界面会思考:AIGUI八要素驱动DingOS实现“感知-生成-进化“闭环
前端·人工智能·ai·gui
豐儀麟阁贵1 小时前
9.3获取字符串信息
java·开发语言·前端·算法
苦夏木禾1 小时前
使用css制作一个环形进度展示圈
前端·css
by__csdn1 小时前
ES6新特性全攻略:JavaScript的现代革命
开发语言·前端·javascript·typescript·ecmascript·es6·js
by__csdn1 小时前
Vue 双向数据绑定深度解析:从原理到实践的全方位指南
前端·javascript·vue.js·typescript·前端框架·vue·ecmascript
奋斗吧程序媛1 小时前
前端 Token 管理与最佳实践
前端·vue.js
linhuai1 小时前
在flutter中dio应该如何封装和使用
前端
汉堡大王95271 小时前
JavaScript类型侦探:四大神器让你一眼看穿变量真身
前端·javascript