# LineChart 折线图/柱状图组件

一个轻量且功能全面的折线/柱状图组件（已测试兼容 uni-app 微信小程序、支付宝小程序、抖音小程序 以及 H5）。

- 多折线与柱状图（通过 `chartType` 切换）
- 缩放（手势/控件）、平移、重置动画
- Tooltip（可配置阈值）、图例（可点击隐藏数据集）
- X 轴自定义格式化、多行标签支持
- 自动/手动 Y 轴范围、柱状图布局与动画

下文为完整的参数说明与若干示例，方便你直接拷贝并在项目中使用。

---

## 快速开始

1. 将 `components/line-chart` 目录复制到你的项目。
2. 在页面中引入并使用组件：

最简示例：

```vue
<template>
  <line-chart :chartData="chartData" />
</template>

<script>
import LineChart from '@/components/line-chart/line-chart.vue'

export default {
  components: { LineChart },
  data() {
    return {
      chartData: {
        labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
        datasets: [
          {
            label: '销售额',
            data: [850, 920, 880, 950, 1000, 980],
            color: '#4A90E2',
            gradientStart: '#4A90E2',
            gradientEnd: '#357ABD'
          }
        ]
      }
    }
  }
}
</script>
```

组件默认会使用合理的样式和动画展示折线图。

---

## 数据格式（chartData）

必填属性 `chartData`：

```javascript
{
  labels: Array<string>, // X 轴标签，长度应与各 dataset.data 数组长度匹配
  datasets: [
    {
      label: string,           // 数据集名称
      data: number[],          // 数值数组
      color?: string,          // 线条或点颜色（十六进制或 css 颜色）
      gradientStart?: string,  // 渐变起始色（可选）
      gradientEnd?: string,    // 渐变结束色（可选）
      // 以下为柱状图相关（仅当 chartType === 'bar' 时可用）
      // barWidth?: number,
      // barColor?: string
    }
  ]
}
```

注意：每个 dataset.data 数组应与 labels 数组长度一致；组件会以数组索引对应的方式进行渲染。

---

## Props（完整参数表）

| 名称 | 类型 | 默认值 | 必填 | 说明 |
|------|------|--------|------|------|
| chartType | String | `'line'` | 否 | 图表类型：`'line'` 或 `'bar'` |
| width | String | `'100%'` | 否 | 图表外层容器宽度（支持百分比或 px） |
| height | String | `'400px'` | 否 | 图表外层容器高度 |
| chartData | Object | - | ✅ | 图表数据（见上文） |
| padding | Object | `{top:20,right:20,bottom:30,left:50}` | 否 | 内边距，见下文 |
| zoomConfig | Object | `{enabled:true,minScale:1,maxScale:10,showControls:true}` | 否 | 缩放配置 |
| animationConfig | Object | `{enabled:true,duration:30}` | 否 | 动画配置（duration 为帧数） |
| yAxisConfig | Object | `{steps:5,autoRange:true,min:null,max:null,allowNegative:true}` | 否 | Y 轴配置 |
| xAxisConfig | Object | `{labelDensity:'auto',showAllLabels:false,formatter:null,lineHeight:14}` | 否 | X 轴配置 |
| tooltipConfig | Object | `{enabled:true,threshold:60}` | 否 | Tooltip 配置 |
| legendConfig | Object | `{enabled:true,position:'bottom',clickable:true}` | 否 | 图例配置 |
| barConfig | Object | 详见下文 | 否 | 柱状图相关配置（当 chartType==='bar' 时生效） |

---

## 配置项详解

padding（内边距）

```javascript
{
  top: 20,
  right: 20,
  bottom: 30,
  left: 50
}
```

zoomConfig（缩放）

```javascript
{
  enabled: true,     // 是否允许缩放/捏合
  minScale: 1,       // 最小缩放倍数
  maxScale: 10,      // 最大缩放倍数
  showControls: true // 是否显示重置缩放按钮
}
```

animationConfig（动画）

```javascript
{
  enabled: true,  // 是否启用动画
  duration: 30    // 动画帧数（默认 30 帧，视设备与 dpr 约 400-600ms）
}
```

yAxisConfig（Y 轴）

```javascript
{
  steps: 5,          // 刻度数量（大致）
  autoRange: true,   // 自动计算范围
  min: null,         // 手动最小值（可选）
  max: null,         // 手动最大值（可选）
  allowNegative: true// 自动计算时是否允许负值
}
```

说明：当 `autoRange: true` 时，组件会根据可见数据自动计算最大/最小值；你也可以单独设置 `min` 或 `max` 覆盖相应端。

xAxisConfig（X 轴）

```javascript
{
  labelDensity: 'auto',   // 'auto' | 'sparse' | 'dense'
  showAllLabels: false,   // true 时强制显示所有标签（可能重叠）
  formatter: null,        // (label, index) => string，用于格式化标签并可返回包含 '\n' 的多行字符串
  lineHeight: 14          // 行高，单位 px，用于多行标签排版
}
```

tooltipConfig（Tooltip）

```javascript
{
  enabled: true,    // 是否显示 tooltip
  threshold: 60     // 触发阈值（像素）——触摸/点击附近多少范围内触发
}
```

legendConfig（图例）

```javascript
{
  enabled: true,        // 是否显示图例
  position: 'bottom',   // 'top' | 'bottom'
  clickable: true       // 点击图例可以切换数据集显示/隐藏
}
```

barConfig（柱状图相关）

```javascript
{
  width: 8,
  innerGap: 2,
  bottomGap: 2,
  leftGap: 2,
  animateMode: 'stagger',      // 'all' | 'stagger'
  staggerDelayFraction: 0.02,  // stagger 模式下列间延迟占比
  borderRadius: { top: 4, bottom: 0 }
}
```

---

## 示例集（更完整）

1) 多折线（带自定义 X 轴 formatter）

```vue
<template>
  <line-chart
    :chartData="chartData"
    :xAxisConfig="{ formatter: formatLabel }"
    width="100%"
    height="320px"
  />
</template>

<script>
import LineChart from '@/components/line-chart/line-chart.vue'

export default {
  components: { LineChart },
  data() {
    return {
      chartData: {
        labels: ['2025-01-01', '2025-01-02', '2025-01-03'],
        datasets: [
          { label: 'A', data: [100, 120, 140], color: '#4A90E2' },
          { label: 'B', data: [80, 110, 130], color: '#E94B3C' }
        ]
      }
    }
  },
  methods: {
    formatLabel(label) {
      // 返回多行文本，中间用 '\n' 分割
      const d = new Date(label)
      const mm = String(d.getMonth() + 1).padStart(2, '0')
      const dd = String(d.getDate()).padStart(2, '0')
      return `${mm}-${dd}\n星期${['日','一','二','三','四','五','六'][d.getDay()]}`
    }
  }
}
</script>
```

2) 切换为柱状图（bar）示例

```vue
<template>
  <line-chart
    chartType="bar"
    :chartData="barData"
    :barConfig="{ width: 12, animateMode: 'all' }"
    width="100%"
    height="320px"
  />
</template>

<script>
import LineChart from '@/components/line-chart/line-chart.vue'

export default {
  components: { LineChart },
  data() {
    return {
      barData: {
        labels: ['1月','2月','3月','4月'],
        datasets: [
          { label: '收入', data: [200, 300, 250, 400], color: '#50C878' }
        ]
      }
    }
  }
}
</script>
```

3) 图例在顶部并关闭 tooltip（示例）

```vue
<line-chart
  :chartData="chartData"
  :legendConfig="{ position: 'top' }"
  :tooltipConfig="{ enabled: false }"
  width="100%"
  height="300px"
/>
```

---

## 交互说明与注意事项

- 缩放：支持双指捏合缩放仅作用于 X 轴；缩放范围受 `zoomConfig.minScale` 与 `zoomConfig.maxScale` 限制。
- 平移：单指拖动沿 X 轴平移，有边界限制，不能平移超出数据区域过多。
- Tooltip：默认由触摸/点击触发，并根据 `tooltipConfig.threshold` 判断触发范围。
- 图例：点击图例会隐藏/显示对应数据集，并会触发 Y 轴范围重计算（如果 `yAxisConfig.autoRange` 为 true）。

兼容性：该组件已在 uni-app 环境下针对不同小程序平台（微信/支付宝/抖音）以及 H5 做了条件编译。

性能建议：当数据点数非常多（>2000）时，建议在数据层做下采样或限制绘制帧率以保持流畅。

---

### legendConfig 图例配置

```javascript
{
  enabled: true,        // 是否显示图例
  position: 'bottom',   // 图例位置：'top' | 'bottom'
  clickable: true       // 是否可点击切换显示/隐藏数据集
}
```

**位置说明**：
- `'bottom'`：图表下方，横排显示（默认）
- `'top'`：图表上方，横排显示

### tooltipConfig Tooltip配置

```javascript
{
  enabled: true,        // 是否启用Tooltip
  threshold: 60         // 触发距离阈值（像素）
}
```

## 完整示例

```vue
<template>
  <view class="page">
    <line-chart
      :chartData="chartData"
      width="100%"
      height="400px"
      :padding="{
        top: 20,
        right: 20,
        bottom: 40,
        left: 50
      }"
      :zoomConfig="{
        enabled: true,
        minScale: 1,
        maxScale: 10,
        showControls: true
      }"
      :animationConfig="{
        enabled: true,
        duration: 60
      }"
      :yAxisConfig="{
        steps: 5,
        autoRange: true
      }"
      :xAxisConfig="{
        labelDensity: 'auto'
      }"
      :tooltipConfig="{
        enabled: true,
        threshold: 60
      }"
    />
    
    <button @tap="updateData">更新数据</button>
  </view>
</template>

<script>
import LineChart from '@/components/line-chart/line-chart.vue'

export default {
  components: {
    LineChart
  },
  
  data() {
    return {
      chartData: {
        labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月'],
        datasets: [
          {
            label: '产品A',
            data: [30, 45, 60, 50, 70, 65, 80, 75],
            color: '#FF6B9D',
            gradientStart: '#FFB6C1',
            gradientEnd: '#FF1493'
          },
          {
            label: '产品B',
            data: [50, 65, 80, 70, 90, 85, 95, 88],
            color: '#4ECDC4',
            gradientStart: '#A8E6CF',
            gradientEnd: '#00CED1'
          }
        ]
      }
    }
  },
  
  methods: {
    updateData() {
      // 更新数据会自动触发过渡动画
      this.chartData = {
        ...this.chartData,
        datasets: [
          {
            ...this.chartData.datasets[0],
            data: this.chartData.datasets[0].data.map(() => 
              Math.floor(Math.random() * 50) + 30
            )
          },
          {
            ...this.chartData.datasets[1],
            data: this.chartData.datasets[1].data.map(() => 
              Math.floor(Math.random() * 50) + 50
            )
          }
        ]
      }
    }
  }
}
</script>
```

## 交互说明

### 未缩放状态（1x）
- **单指移动**：显示Tooltip，查看数据详情
- **双指捏合**：开始放大图表

### 已缩放状态（> 1x）
- **单指快速点击**：显示Tooltip
- **单指拖拽**：左右平移查看不同区域
- **双指捏合**：继续调整缩放级别
- **点击重置按钮**：恢复到1x

### 边界限制
- ✅ 只能在X轴方向缩放和平移
- ✅ Y轴固定不变
- ✅ 数据不会超出Y轴区域
- ✅ 平移范围自动限制在合理区间

## 📋 注意事项

### 数据相关
1. **数据量建议**：建议数据点数量在 5-50 之间，过多可能影响性能
2. **颜色格式**：颜色必须使用十六进制格式（如 `#FF6B9D`）
3. **数据更新**：更新 `chartData` 会自动触发平滑过渡动画
4. **数据一致性**：所有数据集的 `data` 数组长度应与 `labels` 数组长度一致

### 平台相关
1. **微信小程序**：完全支持，使用 type="2d" Canvas，性能更好
2. **支付宝小程序**：完全支持，使用传统 Canvas API（`uni.createCanvasContext`）
3. **抖音小程序**：完全支持，使用 type="2d" Canvas
3. **H5**：完全支持，使用 type="2d" Canvas
4. **其他平台**：理论上支持，但未进行测试

## 📦 组件结构

```
components/line-chart/
├── line-chart.vue          # 主组件文件
└── README.md               # 组件文档
```

## ❓ 常见问题（FAQ）

### 1. 如何更新数据？

直接修改 `chartData` 对象即可，组件会自动触发过渡动画：

```javascript
this.chartData = {
  ...this.chartData,
  datasets: [
    {
      ...this.chartData.datasets[0],
      data: [新数据...]
    }
  ]
}
```

### 2. 如何隐藏某条折线？

启用图例并设置 `clickable: true`，然后点击图例项即可：

```javascript
:legendConfig="{
  enabled: true,
  position: 'bottom',
  clickable: true
}"
```

### 3. 如何自定义 X 轴标签格式？

使用 `xAxisConfig.formatter` 函数：

```javascript
:xAxisConfig="{
  formatter: (label, index) => {
    // 自定义格式化逻辑
    return `格式化后的${label}`
  }
}"
```

### 4. 如何实现多行 X 轴标签？

在 `formatter` 函数中返回包含 `\n` 的字符串：

```javascript
formatter: (label, index) => {
  return `第一行\n第二行`
}
```

### 5. 为什么 Tooltip 不显示？

检查以下几点：
1. `tooltipConfig.enabled` 是否为 `true`
2. 是否在数据点附近点击（默认阈值 60px）
3. 是否在缩放状态下（缩放后需要快速点击）

### 6. 如何禁用缩放功能？

设置 `zoomConfig.enabled` 为 `false`：

```javascript
:zoomConfig="{
  enabled: false
}"
```

### 7. 如何调整 Y 轴刻度数量？

修改 `yAxisConfig.steps`：

```javascript
:yAxisConfig="{
  steps: 10  // 显示 10 个刻度
}"
```

### 8. 如何固定 Y 轴范围？

设置 `yAxisConfig.autoRange` 为 `false` 并指定 `min` 和 `max`：

```javascript
:yAxisConfig="{
  autoRange: false,
  min: 0,
  max: 1000
}"
```

### 9. 数据过渡动画太快/太慢？

动画速度是固定的（约 0.8 秒），暂不支持自定义。如需修改，可以调整组件内的 `transitionProgress += 0.05` 这一行。

### 10. 支持哪些小程序平台？

目前已测试并支持：
- ✅ 微信小程序
- ✅ 支付宝小程序
- ✅ 抖音小程序
- ✅ H5

其他平台理论上也支持，但未经测试。

### 11. 如何修改图表背景色？

组件使用白色背景，如需修改，可以在组件的 `.chart-wrapper` 样式中修改 `background` 属性。

### 12. 如何添加更多数据集？

直接在 `chartData.datasets` 数组中添加即可，支持任意数量：

```javascript
datasets: [
  { label: '产品A', data: [...], color: '#4A90E2' },
  { label: '产品B', data: [...], color: '#E94B3C' },
  { label: '产品C', data: [...], color: '#50C878' },
  { label: '产品D', data: [...], color: '#9C27B0' },
  // 继续添加...
]
```

### 13. Tooltip 显示的数据不准确？

这可能是因为数据过渡动画期间 Tooltip 显示的是过渡中的数据。这是正常的，动画完成后会显示准确数据。

### 14. 如何调整图例位置？

使用 `legendConfig.position`：

```javascript
:legendConfig="{
  position: 'top'  // 或 'bottom'
}"
```

### 15. 如何隐藏重置缩放按钮？

设置 `zoomConfig.showControls` 为 `false`：

```javascript
:zoomConfig="{
  showControls: false
}"
```

## 🎉 总结

LineChart 是一个功能完整、性能优秀、易于使用的折线图组件：

### 核心优势
- ✅ **功能丰富**：支持多折线/柱状、图例、缩放、平移、Tooltip、格式化等
- ✅ **性能优秀**：优化的绘制逻辑，流畅的动画效果
- ✅ **易于使用**：简洁的 API，丰富的配置选项
- ✅ **跨平台**：支持微信、支付宝、抖音小程序
- ✅ **设计精美**：简洁专业的视觉风格

### 适用场景
- 📊 数据可视化
- 📈 趋势分析
- 📉 对比展示
- 📱 移动端报表
- 🎯 业务监控

开始使用 LineChart，让数据可视化更简单！

