当前位置: 首页 > article >正文

鸿蒙OSUniApp打造多功能图表展示组件 #三方框架 #Uniapp

使用UniApp打造多功能图表展示组件

在当前移动应用开发领域,数据可视化已成为不可或缺的一部分。无论是展示销售数据、用户增长趋势还是其他业务指标,一个优秀的图表组件都能有效提升用户体验。UniApp作为一款跨平台开发框架,如何在其中实现功能强大且灵活的图表组件呢?本文将分享我在实际项目中的经验与思考。

为什么选择UniApp开发图表组件

传统的移动应用开发往往面临多端适配的问题。开发团队需要分别为Android、iOS甚至H5端编写不同的代码,这无疑增加了开发成本和维护难度。而UniApp提供"一次开发,多端发布"的能力,特别适合需要跨平台展示数据的应用场景。

在我参与的一个企业数据分析项目中,客户要求应用能够在各种设备上展示相同的数据图表,并且具备交互能力。这正是UniApp的优势所在。

技术选型:echarts-for-uniapp

经过调研,我选择了echarts-for-uniapp作为基础图表库。它是Apache ECharts在UniApp环境下的实现,保留了ECharts强大的功能同时解决了跨平台适配问题。

安装非常简单:

npm install echarts-for-uniapp

组件设计思路

设计一个好的图表组件,需要考虑以下几点:

  1. 高内聚低耦合 - 组件应该是独立的,只接收必要的数据和配置
  2. 易于扩展 - 能够支持多种图表类型
  3. 响应式适配 - 在不同尺寸的设备上都能良好展示
  4. 性能优化 - 处理大量数据时保持流畅

基于这些原则,我设计了一个名为ChartComponent的通用组件。

核心代码实现

首先创建基础组件结构:

<!-- components/chart/chart.vue -->
<template><view class="chart-container" :style="{ height: height, width: width }"><canvas v-if="canvasId" :canvas-id="canvasId" :id="canvasId" class="chart-canvas"></canvas><view v-if="loading" class="loading-mask"><view class="loading-icon"></view></view></view>
</template><script>
import * as echarts from 'echarts-for-uniapp';
import themes from './themes.js';export default {name: 'ChartComponent',props: {// 图表类型:line, bar, pie等type: {type: String,default: 'line'},// 图表数据chartData: {type: Object,required: true},// 图表配置项options: {type: Object,default: () => ({})},// 画布IDcanvasId: {type: String,default: 'chart' + Date.now()},// 图表宽度width: {type: String,default: '100%'},// 图表高度height: {type: String,default: '300px'},// 主题theme: {type: String,default: 'default'}},data() {return {chart: null,loading: true,resizeObserver: null};},watch: {chartData: {handler: 'updateChart',deep: true},options: {handler: 'updateChart',deep: true},theme() {this.initChart();}},mounted() {this.$nextTick(() => {this.initChart();// 监听窗口变化,实现响应式this.resizeObserver = uni.createSelectorQuery().in(this).select('.chart-container').boundingClientRect().exec((res) => {if (res[0]) {const { width, height } = res[0];this.handleResize(width, height);}});// 添加全局窗口变化监听window.addEventListener('resize', this.onWindowResize);});},beforeDestroy() {if (this.chart) {this.chart.dispose();this.chart = null;}window.removeEventListener('resize', this.onWindowResize);},methods: {initChart() {this.loading = true;// 确保上一个实例被销毁if (this.chart) {this.chart.dispose();}// 获取DOM元素uni.createSelectorQuery().in(this).select(`#${this.canvasId}`).fields({ node: true, size: true }).exec((res) => {if (!res[0] || !res[0].node) {console.error('获取canvas节点失败');return;}const canvas = res[0].node;const chart = echarts.init(canvas, themes[this.theme] || '');this.chart = chart;this.updateChart();this.loading = false;});},updateChart() {if (!this.chart) return;const options = this.generateOptions();this.chart.setOption(options, true);// 通知父组件图表已更新this.$emit('chart-ready', this.chart);},generateOptions() {// 根据不同图表类型生成基础配置let baseOptions = {};switch(this.type) {case 'line':baseOptions = this.generateLineOptions();break;case 'bar':baseOptions = this.generateBarOptions();break;case 'pie':baseOptions = this.generatePieOptions();break;// 其他图表类型...default:baseOptions = this.generateLineOptions();}// 合并用户自定义配置return {...baseOptions,...this.options};},generateLineOptions() {const { series = [], xAxis = [], legend = [] } = this.chartData;return {tooltip: {trigger: 'axis'},legend: {data: legend,bottom: 0},grid: {left: '3%',right: '4%',bottom: '10%',top: '8%',containLabel: true},xAxis: {type: 'category',boundaryGap: false,data: xAxis},yAxis: {type: 'value'},series: series.map(item => ({name: item.name,type: 'line',data: item.data,...item}))};},generateBarOptions() {const { series = [], xAxis = [], legend = [] } = this.chartData;return {tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'}},legend: {data: legend,bottom: 0},grid: {left: '3%',right: '4%',bottom: '10%',top: '8%',containLabel: true},xAxis: {type: 'category',data: xAxis},yAxis: {type: 'value'},series: series.map(item => ({name: item.name,type: 'bar',data: item.data,...item}))};},generatePieOptions() {const { series = [] } = this.chartData;return {tooltip: {trigger: 'item',formatter: '{a} <br/>{b}: {c} ({d}%)'},series: [{name: series.name || '数据分布',type: 'pie',radius: '50%',data: series.data || [],emphasis: {itemStyle: {shadowBlur: 10,shadowOffsetX: 0,shadowColor: 'rgba(0, 0, 0, 0.5)'}},...series}]};},handleResize(width, height) {if (this.chart) {this.chart.resize({width,height});}},onWindowResize() {uni.createSelectorQuery().in(this).select('.chart-container').boundingClientRect().exec((res) => {if (res[0]) {const { width, height } = res[0];this.handleResize(width, height);}});}}
};
</script><style scoped>
.chart-container {position: relative;width: 100%;height: 300px;
}
.chart-canvas {width: 100%;height: 100%;
}
.loading-mask {position: absolute;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(255, 255, 255, 0.7);display: flex;justify-content: center;align-items: center;
}
.loading-icon {width: 40px;height: 40px;border: 3px solid #f3f3f3;border-top: 3px solid #3498db;border-radius: 50%;animation: spin 1s linear infinite;
}
@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}
</style>

实际应用案例

在一个企业数据大屏项目中,我需要展示公司全年的销售数据,包括不同地区的销售额对比、月度趋势等。以下是实际使用示例:

<template><view class="dashboard"><view class="chart-item"><text class="chart-title">各地区销售额占比</text><chart-component type="pie" :chartData="regionData" height="350px"@chart-ready="onChartReady"/></view><view class="chart-item"><text class="chart-title">月度销售趋势</text><chart-component type="line" :chartData="trendData" height="350px"/></view><view class="chart-item"><text class="chart-title">产品销量对比</text><chart-component type="bar" :chartData="productData" height="350px"theme="dark"/></view></view>
</template><script>
import ChartComponent from '@/components/chart/chart.vue';export default {components: {ChartComponent},data() {return {regionData: {series: {name: '地区销售额',data: [{value: 1048, name: '华东'},{value: 735, name: '华北'},{value: 580, name: '华南'},{value: 484, name: '西北'},{value: 300, name: '西南'}]}},trendData: {xAxis: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],legend: ['目标', '实际'],series: [{name: '目标',data: [150, 130, 150, 160, 180, 170, 190, 200, 210, 200, 195, 250],smooth: true},{name: '实际',data: [120, 125, 145, 170, 165, 180, 195, 210, 205, 215, 225, 240],smooth: true}]},productData: {xAxis: ['产品A', '产品B', '产品C', '产品D', '产品E'],legend: ['2022年', '2023年'],series: [{name: '2022年',data: [120, 200, 150, 80, 70]},{name: '2023年',data: [150, 180, 200, 135, 90]}]}};},methods: {onChartReady(chart) {console.log('图表实例已就绪', chart);// 可以进行额外的图表实例操作}}
};
</script><style>
.dashboard {padding: 20rpx;
}
.chart-item {background-color: #fff;border-radius: 10rpx;margin-bottom: 20rpx;padding: 20rpx;box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.chart-title {font-size: 32rpx;font-weight: bold;margin-bottom: 20rpx;display: block;color: #333;
}
</style>

性能优化与注意事项

在实际开发中,我遇到并解决了以下问题:

  1. 大数据量渲染卡顿:当数据量超过1000个点时,图表渲染会变慢。解决方法是实现数据抽样或聚合,仅展示关键点。

  2. 高频更新问题:实时数据频繁更新导致性能下降。解决方法是使用节流(Throttle)技术限制更新频率。

  3. Canvas在某些机型上渲染异常:部分低端安卓设备上出现渲染问题。解决方法是提供降级方案,例如表格展示。

  4. 主题适配:不同项目有不同的设计风格。解决方法是创建themes.js文件,预设多种主题配置。

写在最后

通过UniApp开发图表组件,确实能够大幅降低跨平台开发成本。但任何技术都有两面性,开发者需要在特定场景下权衡利弊。

对于高性能要求的专业数据分析应用,可能原生开发仍是更好的选择;而对于大多数业务场景,UniApp + ECharts的组合足以满足需求,且开发效率更高。

希望这篇文章能给正在考虑UniApp数据可视化开发的同学一些参考,也欢迎在评论区分享你的经验和想法。


代码已经过实际项目验证,如有问题欢迎指正。

相关文章:

鸿蒙OSUniApp打造多功能图表展示组件 #三方框架 #Uniapp

使用UniApp打造多功能图表展示组件 在当前移动应用开发领域&#xff0c;数据可视化已成为不可或缺的一部分。无论是展示销售数据、用户增长趋势还是其他业务指标&#xff0c;一个优秀的图表组件都能有效提升用户体验。UniApp作为一款跨平台开发框架&#xff0c;如何在其中实现…...

Chrome浏览器实验性API computePressure的隐私保护机制如何绕过?

一、computePressure API 设计原理与隐私保护机制 1.1 API 设计目标 computePressure是W3C提出的系统状态监控API,旨在: • 提供系统资源状态的抽象指标(非精确值) • 防止通过高精度时序攻击获取用户指纹 • 平衡开发者需求与用户隐私保护 1.2 隐私保护实现方式 // 典…...

RK3588 串行解串板,支持8路GMSL相机

RK3588 支持的 GMSL 相机接入数量取决于所使用的解串板型号及配置方案&#xff1a; ‌xcDeserializer3.0 解串板‌ 可接入最多 ‌8 路 2M GMSL2 相机‌1。 ‌xcDeserializer4.0 解串板‌ 支持 ‌4 路 2M GMSL2 相机‌1。 ‌边缘计算盒解决方案‌ 部分商用方案可实现 ‌4 或 8…...

OracleLinux7.9-ssh问题

有套rac环境&#xff0c;db1主机无法ssh db1和db1-priv&#xff0c;可以ssh登录 db2和db2-priv [rootdb1 ~]# ssh db1 ^C [rootdb1 ~]# ssh db2 Last login: Wed May 14 18:25:19 2025 from db2 [rootdb2 ~]# ssh db2 Last login: Wed May 14 18:25:35 2025 from db1 [rootdb2…...

手机换IP真的有用吗?可以干什么?

在当今数字化时代&#xff0c;网络安全和个人隐私保护日益受到重视。手机作为我们日常生活中不可或缺的工具&#xff0c;其网络活动痕迹往往通过IP地址被记录和追踪。那么&#xff0c;手机换IP真的有用吗&#xff1f;它能为我们带来哪些实际好处&#xff1f;本文将为你一一解答…...

提示词设计模板(基于最佳实践)

1. 任务清晰化 模糊指令 ➜ 明确指令 ❌ "写一篇关于环保的文章" ✅ *"列出5种城市环保措施&#xff0c;并分别说明其对减少碳排放的影响&#xff08;要求&#xff1a;数据支持案例&#xff09;"* 2. 任务步骤化 案例&#xff1a;策划线上营销活动 1.…...

如何实现一个运动会计分系统?(C语言版)

一、需求分析 设计一个运动会计分系统,计分信息包括参加学校,参与项目,性别,名次个数,各个学校获得名次信息。该系统具有以下功能 数据录入: 链表或结构体数组组织数据数据报表: 依照规定的报表格式对数据打印报表数据排序: 按照要求对数据进行统计,含简单统计及综合统计…...

《P4391 [BalticOI 2009] Radio Transmission 无线传输 题解》

题目描述 给你一个字符串 s1​&#xff0c;它是由某个字符串 s2​ 不断自我连接形成的&#xff08;保证至少重复 2 次&#xff09;。但是字符串 s2​ 是不确定的&#xff0c;现在只想知道它的最短长度是多少。 输入格式 第一行一个整数 L&#xff0c;表示给出字符串的长度。…...

tocmat 启动怎么设置 jvm和gc

在生产环境中部署 Java Web 应用时&#xff0c;我们经常需要给 Tomcat 设置 JVM 参数和 GC 策略&#xff0c;以提高性能、稳定性和可观察性。以下是完整教程&#xff1a; 一、Tomcat 设置 JVM 启动参数的方式 1. 修改 startup 脚本&#xff08;推荐&#xff09; 以 Linux 系统…...

[思维模式-37]:什么是事?什么是物?什么事物?如何通过数学的方法阐述事物?

一、基本概念 1、事&#xff08;Event) “事”通常指的是人类在社会生活中的各种活动、行为、事件或情况&#xff0c;具有动态性和过程性&#xff0c;强调的是一种变化、发展或相互作用的流程。 特点 动态性&#xff1a;“事”往往涉及一系列的动作、变化和发展过程。例如&a…...

面向对象设计模式之代理模式详解

文章目录 面向对象设计模式之代理模式详解面向对象思想&#xff1a;现代软件开发的基石代理模式&#xff1a;巧妙的中间层设计JavaScript 语法点与代理模式的结合JavaScript 实现代理模式示例代理模式的应用场景 面向对象设计模式之代理模式详解 在现代软件开发的浩瀚领域中&a…...

C++【STL】(2)string

C【STL】string用法扩展 1. assign&#xff1a;为字符串赋新值 用于替换字符串内容&#xff0c;支持多种参数形式。 常用形式&#xff1a; // 用另一个字符串赋值 str.assign("Hello World");// 用另一个字符串的子串&#xff08;从第6个字符开始&#xff0c;取5…...

嵌入式学习笔记 - STM32 ADC,多重转换,内部参考电压,过采样,逐次逼近原理,采样时间

一 多个ADC器件&#xff0c;多重转换速率 每个型号MCU通常由多个ADC器件&#xff0c;比如STM32F4有三个ADC器件&#xff0c;每个ADC器件有一个最大转换速率&#xff0c;一般为2.4Mhz&#xff0c;即一个ADC器件每秒最多转换2.4M次&#xff0c;两次转换之间需要有时间间隔&#…...

团结引擎 1.5.0 发布,抖音小游戏平台即将开放、Shader Graph功能新增…引擎能力再提升!

「团结引擎 1.5.0」来啦&#xff01;本次技术更新的内容&#xff0c;涵盖了小游戏、团结引擎车机版、OpenHarmony、Shader Graph、Muse Chat、Hub&License、代码升级、Digital Asset Manager for Tuanjie、团结官方开源车模 Sample 几大方向。 小游戏 在 Tuanjie 1.5.0 版…...

如何下载 Microsoft SQL Server Management Studio 2019

SQL Server Management Studio 是什么,为什么你需要它 SSMS 是 Microsoft 用于管理 SQL Server 环境的主要工具。它为 Windows 用户提供了一个图形用户界面,本质上是数据库管理员和开发人员处理 SQL Server 的指挥中心。重点是——尽管你可能认为它与 SQL Server 捆绑在一起…...

【SSL部署与优化​】​​HTTP/2与HTTPS的协同效应

HTTP/2与HTTPS的协同效应&#xff1a;为何HTTP/2强制要求TLS 1.2&#xff1f; HTTP/2是HTTP协议的现代升级版&#xff0c;旨在通过多路复用、头部压缩等技术提升性能。然而&#xff0c;HTTP/2的设计与部署与HTTPS&#xff08;TLS加密&#xff09;紧密相关&#xff0c;甚至强制…...

如何配置activemq,支持使用wss协议连接。

1、到阿里云申请一个证书&#xff0c;通过后下载jks证书。 2、配置activemq&#xff1a; 打开activemq安装目录中“conf/activemq.xml”&#xff0c;增加以下记录&#xff1a; <transportConnectors> <transportConnector name"wss" uri"…...

GO语言内存管理结构

文章目录 1、内存分区1.1、栈&#xff08;Stack&#xff09;1.2、堆&#xff08;Heap&#xff09; 2、堆内存管理结构2.1、内存分配器&#xff08;MCache → MArena → MSpan → MHeap&#xff09;2.2、大小分类&#xff08;Size Class&#xff09;2.3、分配流程 3、垃圾回收&a…...

初学c语言14(指针6)

一.sizeof和strlen的对比 1.sizeof 操作符&#xff0c;计算变量所占空间大小 2.strlen 库函数&#xff0c;函数原型为&#xff1a; 求的是字符串的长度&#xff0c;统计的是“\0”之前的字符个数 二.指针和笔试题解析 补充&#xff1a;数组名的意义 1.sizeof(数组名) 这…...

数字化转型-4A架构之技术架构

4A架构系列文章 数字化转型-4A架构&#xff08;业务架构、应用架构、数据架构、技术架构&#xff09; 数字化转型-4A架构之业务架构 数字化转型-4A架构之应用架构 数字化转型-4A架构之数据架构 数字化转型-4A架构之技术架构 一、 技术架构 Technology Architecture 1. 技…...

什么是SparkONYarn模式

1. 什么是 Spark on YARN&#xff1f; Spark on YARN 是 Apache Spark 的一种部署模式&#xff0c;允许 Spark 应用程序在 Hadoop YARN 集群上运行&#xff0c;充分利用 YARN 的资源管理和调度能力。这种模式将 Spark 与 Hadoop 生态深度集成&#xff0c;使企业能够在同一集群…...

kaggle薅羊毛

参考&#xff1a;https://pytorch-tutorial.readthedocs.io/en/latest/tutorial/chapter05_application/5_1_kaggle/#512-kaggle https://github.com/girls-in-ai/Girls-In-AI/blob/master/machine_learning_diary/data_analysis/kaggle_intro.md 1&#xff0c;code training…...

TCP 三次握手建立连接详解

文章目录 一、三次握手流程1、第一次握手2、第二次握手3、第三次握手 二、引申问题1、报文丢失&#xff0c;会发生什么&#xff1f;1.1、第一次握手丢失1.2、第二次握手丢失1.3、第三次握手丢失 2、为什么 ISN(Initial Sequence Number&#xff0c;初始序列号) 不固定3、为什么…...

高海拔和远距离的人员识别:面部、体型和步态的融合

大家读完就觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 我们解决了在无约束环境中进行全身人体识别的问题。这个问题出现在诸如IARPA高空和远距离生物识别与身份识别&#xff08;BRIAR&#xff09;计划等监视场景中&#xff0c;其中生物识别数据是在长…...

Golang实践录:在go中使用curl实现https请求

之前曾经在一个 golang 工程调用 libcur 实现 https的请求&#xff0c;当前自测是通过的。后来迁移到另一个小系统出现段错误&#xff0c;于是对该模块代码改造&#xff0c;并再次自测。 问题提出 大约2年前&#xff0c;在某golang项目使用libcurl进行https请求&#xff08;参…...

自然语言处理入门级项目——文本分类

文章目录 前言1.数据预处理1.1数据集介绍1.2数据集抽取1.3划分数据集1.4数据清洗1.5数据保存 2.样本的向量化表征2.1词汇表2.2向量化2.3自定义数据集2.4备注 结语 前言 本篇博客主要介绍自然语言处理领域中一个项目案例——文本分类&#xff0c;具体而言就是判断评价属于积极还…...

如何利用大模型对文章进行分段,提高向量搜索的准确性?

利用大模型对文章进行分段以提高向量搜索准确性,需结合文本语义理解、分块策略优化以及向量表示技术。以下是系统性的解决方案: 一、分块策略的核心原则 语义完整性优先 分块需确保每个文本单元在语义上独立且完整。研究表明,当分块内容保持单一主题时,向量嵌入的语义表征能…...

一发入魂:极简解决 SwiftUI 复杂视图未能正确刷新的问题(上)

概述 各位似秃非秃小码农们都知道&#xff0c;在 SwiftUI 中视图是状态的函数&#xff0c;这意味着状态的改变会导致界面被刷新。 但是&#xff0c;对于有些复杂布局的 SwiftUI 视图来说&#xff0c;它们的界面并不能直接映射到对应的状态上去。这就会造成一个问题&#xff1…...

深入理解 Dijkstra 算法:原理、实现与优化

算法核心思想 Dijkstra算法采用贪心策略&#xff0c;其核心思想可以概括为&#xff1a; 初始化&#xff1a;设置起点到自身的距离为0&#xff0c;到其他所有点的距离为无穷大 迭代处理&#xff1a; 从未处理的顶点中选择当前距离起点最近的顶点 标记该顶点为已处理 通过该顶…...

Postman中https和http的区别是什么?

作为每天与API打交道的测试工程师,理解HTTP与HTTPS的区别不仅关乎协议本身,更直接影响测试方案设计。本文将用测试视角揭示二者在Postman中的关键差异,并分享实战排查技巧。 一、协议层本质差异(测试工程师需要知道的底层原理) 1. 安全传输机制对比 特性HTTPHTTPS加密方…...