使用AI从零打造炫酷的智慧城市大屏(开源):React + Recharts 实战分享

一、起因:为什么要做这个项目?

最近在做数据可视化需求时,看了太多千篇一律的后台管理界面,总想着能不能做点更酷的东西。正好看到很多政府和企业的智慧城市指挥中心大屏,那些闪烁的数据、3D 地图、实时图表,科技感爆棚!

于是决定自己撸一个,顺便探索一下现代前端可视化技术的边界。


二、效果展示:先看看成品

🎨 整体布局

  • 左侧面板:经济指标 + 4 种图表(饼图、柱状图、折线图、面积图)
  • 中间地图:3D 网格地图 + 5 个区域标记(西南/中心/西北/东北/东南)
  • 右侧面板:人口民生 + 雷达图 + 指标卡片
  • 底部导航:城市交通、城市安全、人口民生三大模块切换

✨ 核心亮点

  1. 炫酷的 3D 地图效果:透视网格 + 发光区域圆圈 + 浮动标记
  2. 丰富的图表交互:悬停显示详细数据,Tooltip 自定义样式
  3. 流畅的动画效果:Framer Motion 驱动的进场动画 + 数据轮播
  4. 完整的数据流:Mock API + 自动刷新 Hook + 数据轮播组件
  5. 响应式布局:左右面板自适应宽度,图表自动撑满

三、技术栈:用了哪些工具?

技术栈 用途 为什么选它?
React 19 前端框架 最新版本,Hooks 更强大
TypeScript 类型系统 代码提示 + 类型安全
Vite 构建工具 启动快,HMR 秒级更新
Tailwind CSS 样式方案 原子化 CSS,开发效率高
Recharts 图表库 API 简洁,支持 React 组件化
Framer Motion 动画库 声明式动画,效果丝滑
Lucide React 图标库 轻量级,图标漂亮

四、核心功能拆解

📊 1. 自定义 Tooltip(图表交互增强)

痛点:Recharts 默认 Tooltip 样式太朴素,不符合科技大屏的气质。

解决方案:自定义 Tooltip 组件

tsx 复制代码
const CustomTooltip = ({ active, payload, label }: any) => {
  if (active && payload && payload.length) {
    return (
      <div className="bg-[#0a1628]/95 backdrop-blur-md border border-cyan-500/30 rounded-lg p-3 shadow-[0_0_20px_rgba(0,229,255,0.3)]">
        <p className="text-cyan-400 text-xs font-bold mb-2">{label}</p>
        {payload.map((entry: any, index: number) => (
          <div key={index} className="flex items-center gap-2">
            <div className="w-2 h-2 rounded-full" style={{ backgroundColor: entry.color }} />
            <span className="text-white">{entry.name}: {entry.value}</span>
          </div>
        ))}
      </div>
    );
  }
  return null;
};

效果

  • 深色半透明背景 + 发光边框
  • 彩色指示点与图表颜色对应
  • 平滑的淡入淡出动画

🗺️ 2. 3D 地图效果(视觉核心)

实现思路

  1. 透视网格 :使用 CSS transform: perspective() rotateX() 创建 3D 感
  2. 发光圆圈 :每个区域用渐变圆圈 + blur 阴影 + animate-pulse
  3. 浮动标记:自定义 3D 金字塔 SVG + 上下浮动动画
tsx 复制代码
// 透视网格
<div style={{
  background: `
    linear-gradient(rgba(0, 229, 255, 0.1) 1px, transparent 1px), 
    linear-gradient(90deg, rgba(0, 229, 255, 0.1) 1px, transparent 1px)
  `,
  backgroundSize: '60px 60px',
  transform: 'perspective(1000px) rotateX(70deg) translateY(-200px) scale(1.5)',
  maskImage: 'radial-gradient(circle at center, black 0%, transparent 70%)',
}} />

技巧

  • maskImage 实现边缘渐隐效果
  • 5 个区域圆圈位置精确对应地图标记
  • 标记自带信息卡片,悬停放大

🔄 3. 数据自动刷新(实战 Hooks)

需求:模拟实时数据更新,每 5 秒刷新一次。

自定义 Hook

tsx 复制代码
export function useDataRefresh<T>(
  fetchFunction: () => Promise<T>,
  interval: number = 5000,
  initialData: T
) {
  const [data, setData] = useState<T>(initialData);
  const [loading, setLoading] = useState(false);

  const fetchData = useCallback(async () => {
    try {
      setLoading(true);
      const result = await fetchFunction();
      setData(result);
    } catch (err) {
      console.error('Data fetch error:', err);
    } finally {
      setLoading(false);
    }
  }, [fetchFunction]);

  useEffect(() => {
    fetchData(); // 初始加载
    const timer = setInterval(fetchData, interval); // 定时刷新
    return () => clearInterval(timer);
  }, [fetchData, interval]);

  return { data, loading, refresh: fetchData };
}

使用方式

tsx 复制代码
const { data, loading, refresh } = useDataRefresh(
  fetchMetrics, // Mock API 函数
  5000,         // 刷新间隔
  []            // 初始数据
);

🎠 4. 数据轮播组件(动态展示)

场景:首页需要轮播展示多个重点指标。

实现要点

  1. useDataCarousel Hook:管理轮播逻辑
  2. AnimatePresence:切换时的淡入淡出动画
  3. 控制按钮:上一个/下一个/播放/暂停
tsx 复制代码
const { currentData, next, prev, pause, play, isPlaying } = 
  useDataCarousel(dataList, 3000);

<AnimatePresence mode="wait">
  <motion.div
    key={currentIndex}
    initial={{ opacity: 0, x: 50 }}
    animate={{ opacity: 1, x: 0 }}
    exit={{ opacity: 0, x: -50 }}
    transition={{ duration: 0.5 }}
  >
    {/* 数据内容 */}
  </motion.div>
</AnimatePresence>

亮点

  • 支持手动控制和自动播放
  • 轮播指示器实时同步
  • 数据切换动画流畅自然

📡 5. Mock API 数据服务

为什么需要 Mock

  • 前端开发阶段后端接口未就绪
  • 方便演示和测试
  • 模拟随机数据,更真实

API 设计

tsx 复制代码
// Mock API: 获取指标数据
export const fetchMetrics = async (): Promise<MetricData[]> => {
  await delay(500); // 模拟网络延迟
  return [
    { 
      title: '公共预算收入', 
      value: random(500, 600).toString(), 
      unit: '亿', 
      trend: 'up', 
      percentage: random(20, 30) 
    },
    // ... 更多数据
  ];
};

完整 API 列表

  • fetchMetrics() - 顶部指标卡片
  • fetchLineChartData() - 折线图
  • fetchPieData() - 饼图
  • fetchBarChartData() - 柱状图
  • fetchAreaChartData() - 面积图
  • fetchRadarData() - 雷达图
  • fetchMapMarkers() - 地图标记

🎨 6. 响应式布局方案

挑战:大屏通常是固定分辨率,但需要适配不同屏幕。

方案对比

方案 优点 缺点 适用场景
autofit.js 等比缩放,保持比例 小屏幕可能有黑边 固定比例大屏
Flexbox + 百分比 充分利用空间 需要精细调整 自适应布局
CSS Grid 布局灵活 学习成本高 复杂网格

我的选择:Flexbox + 百分比宽度

tsx 复制代码
// 左右面板自适应
<div className="w-[22%] min-w-[320px] max-w-[400px]">
  {/* 面板内容 */}
</div>

// 图表区域撑满
<div className="flex-1 min-h-0 overflow-y-auto">
  {/* 图表列表 */}
</div>

技巧

  • flex-1 + min-h-0 解决 flex 子元素溢出
  • overflow-y-auto 让图表区域可滚动
  • ResponsiveContainer 让图表自动适应容器

五、踩坑实录

🐛 坑 1:Recharts 图表不撑满容器

现象:图表固定高度,无法充满 flex 容器。

原因ResponsiveContainer 需要明确的高度。

解决

tsx 复制代码
// ❌ 错误写法
<div className="flex-1">
  <ResponsiveContainer width="100%" height="100%">
    <LineChart data={data} />
  </ResponsiveContainer>
</div>

// ✅ 正确写法
<div className="flex-1 min-h-0"> {/* 关键:min-h-0 */}
  <ResponsiveContainer width="100%" height="100%">
    <LineChart data={data} />
  </ResponsiveContainer>
</div>

🐛 坑 2:Framer Motion 动画闪烁

现象:列表项动画时会闪烁或重复。

原因 :没有设置唯一的 key

解决

tsx 复制代码
<AnimatePresence mode="wait"> {/* mode="wait" 很重要 */}
  <motion.div key={currentIndex}> {/* key 必须唯一 */}
    {/* 内容 */}
  </motion.div>
</AnimatePresence>

🐛 坑 3:地图标记位置不准

现象:标记偏离预期位置。

原因:绝对定位的基准点是左上角,而不是标记中心。

解决

tsx 复制代码
<div style={{ left: x, top: y }}> {/* 左上角定位 */}
  <div className="transform -translate-x-1/2 -translate-y-1/2"> {/* 居中偏移 */}
    {/* 标记内容 */}
  </div>
</div>

六、性能优化建议

⚡ 1. 图表按需加载

tsx 复制代码
import { LineChart } from 'recharts'; // ✅ 具名导入
// 而不是 import * as Recharts from 'recharts'; // ❌

⚡ 2. 动画节流

tsx 复制代码
// 使用 Framer Motion 的 layout 模式
<motion.div layout layoutId="card">

⚡ 3. 数据缓存

tsx 复制代码
const [cachedData, setCachedData] = useState({});
// 相同请求返回缓存结果

七、未来计划

  • WebSocket 实时数据推送:替换定时轮询
  • 可配置主题:支持多种颜色方案
  • 其他界面:增加其他tab大屏

八、总结

这个项目最大的收获是:

  1. Recharts 不只是画图:结合 TypeScript + 自定义组件,可以实现高度定制化
  2. Hooks 真香useDataRefreshuseDataCarousel 可以复用到任何项目
  3. CSS 动画比想象中强大perspective + blur + animate-pulse 就能做出酷炫效果
  4. Mock 数据是好习惯:前后端分离开发效率翻倍

如果你也在做数据可视化项目,希望这篇文章能给你一些启发!


九、快速上手

📦 安装依赖

bash 复制代码
npm install react recharts framer-motion lucide-react
npm install -D tailwindcss @tailwindcss/vite

🚀 启动项目

bash 复制代码
npm run dev

📂 项目结构

bash 复制代码
src/
├── api/
│   └── mockData.ts          # Mock API 服务
├── hooks/
│   └── useDataRefresh.ts    # 数据刷新 Hook
├── components/
│   └── DataCarouselDemo.tsx # 轮播组件
└── App.tsx                  # 主应用

本文所有代码均可商用,欢迎参考和学习!

我放在公众号(柳杉前端) 回复 智慧城市大屏 获取源码

#前端开发 #数据可视化 #React #智慧城市 #大屏设计

相关推荐
王码码20351 小时前
Flutter for OpenHarmony:web_socket 纯 Dart 标准 WebSocket 客户端(跨平台兼容性之王) 深度解析与鸿蒙
android·前端·websocket·网络协议·flutter·华为·harmonyos
Highcharts.js2 小时前
玩转Highcharts气泡图|从散点图到气泡图:增加一个维度,数据可视化瞬间立体起来
javascript·信息可视化·散点图·highcharts·图表开发·气泡图·图表创建
A_B_C_Q2 小时前
StringBuilder 与 StringBuffer的区别
java·前端
颜酱2 小时前
差分数组:高效处理数组区间批量更新的核心技巧
javascript·后端·算法
洋洋技术笔记3 小时前
vue3+vite+elementplus简单介绍
前端
Joker Zxc3 小时前
【前端基础(Javascript部分)】2、JavaScript的变量和数据类型
开发语言·前端·javascript
yuki_uix3 小时前
别再死记优缺点了:聊聊 REST、GraphQL、WebSocket 的使用场景
前端
We་ct3 小时前
LeetCode 173. 二叉搜索树迭代器:BSTIterator类 实现与解析
前端·算法·leetcode·typescript
weixin_395448913 小时前
main.c_0222cursor
c语言·前端·算法