uCharts组件学习
一、uCharts组件概述
1.1 uCharts组件介绍
uCharts是一款基于Canvas开发的跨平台图表组件,支持H5、小程序、APP等多端使用。
主要特点:
- 跨平台兼容
- 性能优越
- 配置灵活
- 类型丰富
1.2 安装与引入
javascript
// 在uni-app项目中安装
npm install @qiun/ucharts
// 在页面中引入
import uCharts from '@qiun/ucharts'
二、图表基本结构
2.1 基本页面结构
html
<!-- 图表容器 -->
<view class="chart-container">
<qiun-data-charts
type="column"
:chartData="chartData"
:opts="chartOptions"
canvasId="columnChart"
canvas2d
/>
</view>
javascript
// 页面脚本
export default {
data() {
return {
// 图表数据
chartData: {},
// 图表配置
chartOptions: {}
}
},
onLoad() {
this.initChartData()
},
methods: {
initChartData() {
// 初始化图表数据
}
}
}
css
/* 图表样式 */
.chart-container {
width: 100%;
height: 500rpx;
}
三、绘制柱状图
3.1 定义柱状图数据
javascript
// 柱状图数据结构
const columnChartData = {
categories: ['一月', '二月', '三月', '四月', '五月', '六月'],
series: [
{
name: '销量',
data: [35, 48, 25, 38, 45, 30]
}
]
}
3.2 完整柱状图案例
html
<template>
<view class="chart-wrapper">
<qiun-data-charts
type="column"
:chartData="columnData"
:opts="columnOpts"
canvasId="demoColumn"
canvas2d
/>
</view>
</template>
<script>
export default {
data() {
return {
columnData: {},
columnOpts: {
// 图表配置
}
}
},
onLoad() {
this.initColumnChart()
},
methods: {
initColumnChart() {
// 模拟数据
this.columnData = {
categories: ['苹果', '三星', '小米', 'OPPO', 'vivo', '华为'],
series: [
{
name: '手机销量',
data: [7033, 5655, 4332, 3455, 2878, 1954],
color: '#2B7EFB' // 自定义柱状图颜色
}
]
}
// 图表配置选项
this.columnOpts = {
color: ['#2B7EFB'], // 系列颜色
padding: [15, 15, 0, 5], // 画布填充边距
enableScroll: false, // 禁用滚动
legend: {
show: true, // 显示图例
position: 'top', // 图例位置
float: 'center', // 对齐方式
itemGap: 20 // 图例间距
},
xAxis: {
disableGrid: false, // 显示网格线
boundaryGap: 'justify', // 坐标轴两边留白策略
axisLine: true, // 显示坐标轴线
axisTick: true // 显示坐标轴刻度
},
yAxis: {
data: [
{
min: 0, // 最小值
max: 8000 // 最大值
}
],
gridType: 'solid', // 网格线类型
dashLength: 4, // 虚线间隔
gridColor: '#CCCCCC', // 网格线颜色
calibration: true // 显示刻度
},
extra: {
column: {
type: 'group', // 柱状图类型,group为分组柱状图
width: 20, // 柱状图宽度
activeBgColor: '#000000', // 激活背景色
activeBgOpacity: 0.08, // 激活背景透明度
linearType: 'custom', // 渐变类型
linearOpacity: 1, // 渐变透明度
customColor: ['#2B7EFB'] // 自定义颜色
}
}
}
}
}
}
</script>
<style>
.chart-wrapper {
width: 100%;
height: 600rpx;
background: #ffffff;
border-radius: 10rpx;
padding: 20rpx;
box-sizing: border-box;
}
</style>
四、绘制折线图
4.1 折线图数据结构
javascript
// 折线图数据格式
const lineChartData = {
categories: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
series: [
{
name: '访问量',
data: [125, 358, 420, 235, 487, 612, 389]
}
]
}
4.2 完整折线图案例
html
<template>
<view class="chart-wrapper">
<qiun-data-charts
type="line"
:chartData="lineData"
:opts="lineOpts"
canvasId="demoLine"
canvas2d
/>
</view>
</template>
<script>
export default {
data() {
return {
lineData: {},
lineOpts: {}
}
},
onLoad() {
this.initLineChart()
},
methods: {
initLineChart() {
// 折线图数据
this.lineData = {
categories: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
series: [
{
name: '销售额',
data: [12500, 15800, 14200, 13500, 17800, 21200, 19800, 22500, 21800, 24500, 23200, 25800],
color: '#FF6B6B'
},
{
name: '成本',
data: [8500, 9200, 8800, 9100, 10500, 12800, 11500, 13200, 12600, 14200, 13500, 14800],
color: '#4ECDC4'
}
]
}
// 折线图配置
this.lineOpts = {
color: ['#FF6B6B', '#4ECDC4'],
padding: [15, 15, 0, 5],
enableScroll: false,
legend: {
show: true,
position: 'top',
float: 'center',
itemGap: 30
},
xAxis: {
disableGrid: true,
boundaryGap: 'justify',
axisLine: true,
axisTick: true,
fontSize: 10
},
yAxis: {
gridType: 'dash',
dashLength: 4,
gridColor: '#E5E5E5',
data: [{
min: 0,
max: 30000,
format: (val) => {
return (val / 1000).toFixed(0) + 'k' // 格式化Y轴显示
}
}],
calibration: true
},
extra: {
line: {
type: 'curve', // 曲线类型,straight为直线,curve为曲线
width: 2, // 线宽
activeType: 'hollow', // 点激活样式
animation: 'vertical', // 动画方向
points: {
radius: 3, // 点半径
strokeWidth: 1 // 点描边宽度
}
}
}
}
}
}
}
</script>
五、绘制饼图
5.1 饼图数据结构
javascript
// 饼图数据格式
const pieChartData = {
series: [
{
name: '分类一',
data: 35
},
{
name: '分类二',
data: 25
},
{
name: '分类三',
data: 18
},
{
name: '分类四',
data: 22
}
]
}
5.2 完整饼图案例
html
<template>
<view class="chart-wrapper">
<qiun-data-charts
type="pie"
:chartData="pieData"
:opts="pieOpts"
canvasId="demoPie"
canvas2d
/>
</view>
</template>
<script>
export default {
data() {
return {
pieData: {},
pieOpts: {}
}
},
onLoad() {
this.initPieChart()
},
methods: {
initPieChart() {
// 饼图数据
this.pieData = {
series: [
{
name: '技术研发',
data: 40,
color: '#FF6B6B'
},
{
name: '市场营销',
data: 25,
color: '#4ECDC4'
},
{
name: '行政管理',
data: 15,
color: '#45B7D1'
},
{
name: '客户服务',
data: 10,
color: '#FFA07A'
},
{
name: '其他',
data: 10,
color: '#98D8C8'
}
]
}
// 饼图配置
this.pieOpts = {
color: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8'],
padding: [5, 5, 5, 5],
extra: {
pie: {
activeOpacity: 0.5, // 激活透明度
activeRadius: 10, // 激活半径
offsetAngle: 0, // 起始角度
labelWidth: 15, // 标签宽度
border: false, // 是否显示边框
borderWidth: 1, // 边框宽度
borderColor: '#FFFFFF', // 边框颜色
linearType: 'custom', // 渐变类型
customColor: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8'] // 自定义颜色
},
tooltip: {
showBox: true, // 显示提示框
showCategory: true // 显示分类
}
},
legend: {
show: true,
position: 'right',
float: 'center',
lineHeight: 25
},
title: {
name: '部门预算分布',
fontSize: 14,
color: '#666666'
}
}
}
}
}
</script>
六、绘制混合图
6.1 混合图数据结构
javascript
// 混合图数据格式(柱状图+折线图)
const mixChartData = {
categories: ['一月', '二月', '三月', '四月', '五月', '六月'],
series: [
{
name: '销量',
data: [35, 48, 25, 38, 45, 30],
type: 'column' // 柱状图
},
{
name: '增长率',
data: [15, 25, 10, 20, 18, 12],
type: 'line' // 折线图
}
]
}
6.2 完整混合图案例
html
<template>
<view class="chart-wrapper">
<qiun-data-charts
type="mix"
:chartData="mixData"
:opts="mixOpts"
canvasId="demoMix"
canvas2d
/>
</view>
</template>
<script>
export default {
data() {
return {
mixData: {},
mixOpts: {}
}
},
onLoad() {
this.initMixChart()
},
methods: {
initMixChart() {
// 混合图数据
this.mixData = {
categories: ['Q1', 'Q2', 'Q3', 'Q4'],
series: [
{
name: '收入',
data: [350, 420, 380, 480],
type: 'column', // 柱状图系列
color: '#2B7EFB'
},
{
name: '利润',
data: [120, 180, 150, 220],
type: 'column', // 柱状图系列
color: '#4ECDC4'
},
{
name: '利润率',
data: [34.3, 42.9, 39.5, 45.8],
type: 'line', // 折线图系列
color: '#FF6B6B'
}
]
}
// 混合图配置
this.mixOpts = {
color: ['#2B7EFB', '#4ECDC4', '#FF6B6B'],
padding: [15, 15, 0, 5],
enableScroll: false,
legend: {
show: true,
position: 'top',
float: 'center',
itemGap: 25
},
xAxis: {
disableGrid: false,
boundaryGap: 'justify',
axisLine: true,
axisTick: true
},
yAxis: {
gridType: 'solid',
dashLength: 4,
gridColor: '#CCCCCC',
data: [
{
min: 0,
max: 500,
format: (val) => {
return val + '万'
}
},
{
min: 0,
max: 50,
position: 'right',
format: (val) => {
return val + '%'
}
}
],
calibration: true
},
extra: {
column: {
type: 'group',
width: 20,
linearType: 'custom',
linearOpacity: 1
},
line: {
type: 'straight',
width: 2,
yAxisIndex: 1, // 使用第二个Y轴
points: {
radius: 3
}
}
}
}
}
}
}
</script>
七、综合性案例
7.1 多图表仪表盘
html
<template>
<view class="dashboard">
<!-- 标题 -->
<view class="dashboard-title">销售数据仪表盘</view>
<!-- 数据概览 -->
<view class="stats-overview">
<view class="stat-item">
<text class="stat-value">¥258,000</text>
<text class="stat-label">年度销售额</text>
</view>
<view class="stat-item">
<text class="stat-value">45.8%</text>
<text class="stat-label">利润率</text>
</view>
<view class="stat-item">
<text class="stat-value">12,580</text>
<text class="stat-label">订单数量</text>
</view>
</view>
<!-- 图表容器 -->
<view class="charts-container">
<!-- 月度销售趋势 -->
<view class="chart-card">
<view class="chart-title">月度销售趋势</view>
<qiun-data-charts
type="line"
:chartData="monthlyData"
:opts="monthlyOpts"
canvasId="monthlyTrend"
canvas2d
/>
</view>
<!-- 产品类别分布 -->
<view class="chart-card">
<view class="chart-title">产品类别分布</view>
<qiun-data-charts
type="pie"
:chartData="categoryData"
:opts="categoryOpts"
canvasId="categoryDist"
canvas2d
/>
</view>
<!-- 区域销售对比 -->
<view class="chart-card">
<view class="chart-title">区域销售对比</view>
<qiun-data-charts
type="column"
:chartData="regionData"
:opts="regionOpts"
canvasId="regionCompare"
canvas2d
/>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 月度趋势数据
monthlyData: {},
monthlyOpts: {},
// 类别分布数据
categoryData: {},
categoryOpts: {},
// 区域对比数据
regionData: {},
regionOpts: {}
}
},
onLoad() {
this.initDashboard()
},
methods: {
initDashboard() {
this.initMonthlyTrend()
this.initCategoryDistribution()
this.initRegionComparison()
},
// 初始化月度趋势
initMonthlyTrend() {
this.monthlyData = {
categories: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
series: [
{
name: '销售额',
data: [18500, 15800, 24200, 19500, 27800, 31200, 29800, 32500, 31800, 34500, 33200, 35800],
color: '#2B7EFB'
},
{
name: '同比增长',
data: [12, 8, 25, 15, 32, 28, 25, 30, 28, 35, 32, 38],
color: '#FF6B6B'
}
]
}
this.monthlyOpts = {
color: ['#2B7EFB', '#FF6B6B'],
padding: [15, 15, 0, 5],
legend: {
show: true,
position: 'top'
},
xAxis: {
disableGrid: true
},
yAxis: {
data: [
{
min: 0,
max: 40000,
format: (val) => (val / 1000).toFixed(0) + 'k'
},
{
min: 0,
max: 50,
position: 'right',
format: (val) => val + '%'
}
]
},
extra: {
line: {
type: 'curve',
width: 2,
yAxisIndex: 1
}
}
}
},
// 初始化类别分布
initCategoryDistribution() {
this.categoryData = {
series: [
{ name: '电子产品', data: 35, color: '#2B7EFB' },
{ name: '家居用品', data: 25, color: '#4ECDC4' },
{ name: '服装鞋帽', data: 20, color: '#FF6B6B' },
{ name: '食品饮料', data: 15, color: '#45B7D1' },
{ name: '其他', data: 5, color: '#98D8C8' }
]
}
this.categoryOpts = {
padding: [5, 5, 5, 5],
extra: {
pie: {
labelWidth: 15
}
},
legend: {
show: true,
position: 'right'
}
}
},
// 初始化区域对比
initRegionComparison() {
this.regionData = {
categories: ['华东', '华南', '华北', '西南', '西北', '东北'],
series: [
{
name: '2023年',
data: [85000, 72000, 68000, 55000, 42000, 38000],
color: '#2B7EFB'
},
{
name: '2022年',
data: [72000, 65000, 58000, 48000, 35000, 32000],
color: '#4ECDC4'
}
]
}
this.regionOpts = {
color: ['#2B7EFB', '#4ECDC4'],
padding: [15, 15, 0, 5],
legend: {
show: true,
position: 'top'
},
xAxis: {
disableGrid: false
},
yAxis: {
data: [{
min: 0,
max: 100000,
format: (val) => (val / 1000).toFixed(0) + 'k'
}]
},
extra: {
column: {
type: 'group',
width: 20
}
}
}
}
}
}
</script>
<style scoped>
.dashboard {
padding: 20rpx;
background: #f5f5f5;
}
.dashboard-title {
font-size: 36rpx;
font-weight: bold;
text-align: center;
margin-bottom: 30rpx;
color: #333;
}
.stats-overview {
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
background: white;
border-radius: 10rpx;
padding: 30rpx;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.stat-value {
font-size: 32rpx;
font-weight: bold;
color: #2B7EFB;
margin-bottom: 10rpx;
}
.stat-label {
font-size: 24rpx;
color: #666;
}
.charts-container {
display: flex;
flex-direction: column;
gap: 30rpx;
}
.chart-card {
background: white;
border-radius: 10rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}
.chart-title {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
}
</style>
7.2 交互式图表组件
html
<template>
<view class="interactive-chart">
<!-- 图表控制面板 -->
<view class="control-panel">
<view class="control-group">
<text class="control-label">图表类型:</text>
<picker @change="onChartTypeChange" :value="chartTypeIndex" :range="chartTypes">
<view class="picker">{{ chartTypes[chartTypeIndex] }}</view>
</picker>
</view>
<view class="control-group">
<text class="control-label">数据范围:</text>
<picker @change="onDataRangeChange" :value="dataRangeIndex" :range="dataRanges">
<view class="picker">{{ dataRanges[dataRangeIndex] }}</view>
</picker>
</view>
<button class="refresh-btn" @tap="refreshData">刷新数据</button>
</view>
<!-- 图表显示区域 -->
<view class="chart-area">
<qiun-data-charts
:type="currentChartType"
:chartData="dynamicData"
:opts="dynamicOpts"
:canvasId="canvasId"
canvas2d
/>
</view>
<!-- 数据提示 -->
<view class="data-tips" v-if="selectedData">
<text>选中数据: {{ selectedData.name }} - {{ selectedData.value }}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
chartTypes: ['柱状图', '折线图', '饼图'],
chartTypeIndex: 0,
dataRanges: ['最近7天', '最近30天', '最近90天'],
dataRangeIndex: 0,
currentChartType: 'column',
dynamicData: {},
dynamicOpts: {},
selectedData: null,
canvasId: 'interactiveChart'
}
},
onLoad() {
this.initChart()
},
methods: {
initChart() {
this.generateData()
this.setChartOptions()
},
// 生成模拟数据
generateData() {
const range = this.dataRanges[this.dataRangeIndex]
let categories = []
let seriesData = []
switch(range) {
case '最近7天':
categories = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
seriesData = this.generateRandomData(7, 100, 500)
break
case '最近30天':
categories = Array.from({length: 30}, (_, i) => `${i+1}号`)
seriesData = this.generateRandomData(30, 80, 400)
break
case '最近90天':
categories = Array.from({length: 12}, (_, i) => `第${i+1}周`)
seriesData = this.generateRandomData(12, 2000, 8000)
break
}
if (this.currentChartType === 'pie') {
this.dynamicData = {
series: categories.map((name, index) => ({
name,
data: seriesData[index],
color: this.getColor(index)
}))
}
} else {
this.dynamicData = {
categories,
series: [{
name: '数据值',
data: seriesData,
color: '#2B7EFB'
}]
}
}
},
// 生成随机数据
generateRandomData(count, min, max) {
return Array.from({length: count}, () =>
Math.floor(Math.random() * (max - min + 1)) + min
)
},
// 获取颜色
getColor(index) {
const colors = ['#2B7EFB', '#4ECDC4', '#FF6B6B', '#45B7D1', '#FFA07A', '#98D8C8']
return colors[index % colors.length]
},
// 设置图表配置
setChartOptions() {
const baseOpts = {
color: ['#2B7EFB'],
padding: [15, 15, 0, 5],
legend: {
show: true,
position: 'top'
},
extra: {
tooltip: {
showBox: true
}
}
}
if (this.currentChartType === 'column') {
this.dynamicOpts = {
...baseOpts,
xAxis: { disableGrid: false },
yAxis: {
data: [{ min: 0 }],
gridType: 'solid'
},
extra: {
...baseOpts.extra,
column: {
type: 'group',
width: 20
}
}
}
} else if (this.currentChartType === 'line') {
this.dynamicOpts = {
...baseOpts,
xAxis: { disableGrid: true },
yAxis: {
data: [{ min: 0 }],
gridType: 'dash'
},
extra: {
...baseOpts.extra,
line: {
type: 'curve',
width: 2
}
}
}
} else if (this.currentChartType === 'pie') {
this.dynamicOpts = {
...baseOpts,
padding: [5, 5, 5, 5],
extra: {
...baseOpts.extra,
pie: {
labelWidth: 15
}
},
legend: {
show: true,
position: 'right'
}
}
}
},
// 图表类型变更
onChartTypeChange(e) {
this.chartTypeIndex = e.detail.value
const types = ['column', 'line', 'pie']
this.currentChartType = types[this.chartTypeIndex]
this.initChart()
},
// 数据范围变更
onDataRangeChange(e) {
this.dataRangeIndex = e.detail.value
this.initChart()
},
// 刷新数据
refreshData() {
this.generateData()
}
}
}
</script>
<style scoped>
.interactive-chart {
padding: 20rpx;
}
.control-panel {
background: white;
border-radius: 10rpx;
padding: 30rpx;
margin-bottom: 30rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
align-items: center;
}
.control-group {
display: flex;
align-items: center;
margin-right: 30rpx;
}
.control-label {
font-size: 28rpx;
color: #333;
margin-right: 15rpx;
}
.picker {
padding: 15rpx 30rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
background: #f9f9f9;
}
.refresh-btn {
background: #2B7EFB;
color: white;
border: none;
border-radius: 8rpx;
padding: 15rpx 30rpx;
font-size: 28rpx;
}
.chart-area {
background: white;
border-radius: 10rpx;
padding: 30rpx;
height: 600rpx;
}
.data-tips {
background: #e8f4ff;
border: 1rpx solid #2B7EFB;
border-radius: 8rpx;
padding: 20rpx;
margin-top: 20rpx;
text-align: center;
color: #2B7EFB;
font-size: 28rpx;
}
</style>
八、模块小结
8.1 核心知识点总结
-
图表类型选择
- column: 柱状图
- line: 折线图
- pie: 饼图
- mix: 混合图
-
数据结构规范
- categories: X轴分类数据
- series: 系列数据数组
- 每个系列包含name、data、color等属性
-
配置选项要点
- color: 颜色配置
- padding: 内边距
- legend: 图例配置
- xAxis/yAxis: 坐标轴配置
- extra: 额外配置,针对不同图表类型
-
最佳实践
- 合理设置canvasId避免冲突
- 使用canvas2d提升性能
- 适当的数据格式化
- 响应式设计考虑
8.2 常见问题解决
-
图表不显示
- 检查canvasId是否唯一
- 确认数据格式正确
- 验证组件引入路径
-
性能优化
- 大数据集启用滚动
- 合理设置动画效果
- 避免频繁重绘
-
样式定制
- 通过opts配置样式
- 使用customColor自定义颜色
- 调整padding控制布局
通过本指南的学习,您应该能够熟练使用uCharts组件创建各种类型的图表,并实现复杂的交互功能。