uniapp项目使用官网echarts(h5)以及移动端问题解决方案(最后有完整实例代码)

一、效果图

1.终端安装

npm install echarts --save

2.在使用页面引入echarts

import *as echarts from 'echarts'

2.在使用页面引入echarts

问题一:区别pc端,移动端要用canvas布局,不要用div

	<!-- ECharts 容器 -->
			<view class="charts-box" @tap="handleTap">
				<canvas style="width:100%;height: 100%;zoom: 1 ;" id="mychart1" ref="echartsRef" canvas-id="mychart"
					:canvas-type="canvasType">
				</canvas>
			</view>

问题二:tooltip弹窗点击不显示 、保证点击事件和坐标轴不能错位(上文布局中我已经使用了@tap="handleTap"绑定了事件
下面就是核心代码:通过uni.createSelectorQuery获取创建的元素)

<script module="echartsScript" lang="renderjs">
		handleTap(event) {
				this.onClick(event);
			},
			onClick(event) {
				const query = uni.createSelectorQuery().in(this);
				query.select('#mychart1').boundingClientRect((rect) => {
					if (rect) {
						const x = event.detail.x - rect.left; // 修正为相对于画布的 X 坐标
						const y = event.detail.y - rect.top; // 修正为相对于画布的 Y 坐标

						const xIndex = this.myEcharts.convertFromPixel({
							seriesIndex: 0
						}, [x, y])[0];

						this.myEcharts.dispatchAction({
							type: 'showTip',
							seriesIndex: 0,
							dataIndex: xIndex
						});
					}
				}).exec();
			},


<script/>

问题三:echarts显示文字不清晰、:通过svg渲染

const ctx = document.getElementById('mychart1');
				const canvasWidth = ctx.offsetWidth;
				const canvasHeight = ctx.offsetHeight;

				this.myEcharts = echarts.init(ctx, null, {
					width: canvasWidth,
					height: canvasHeight,
					renderer: 'svg'
				});

问题四 :echarts使用rpx单位,使用字符串形式

	left: '40rpx',

问题五 :使用resize事件进行实时布局适配

			window.addEventListener('resize', this.handleResize);



          handleResize() {
				this.$nextTick(() => {
					if (this.myEcharts) {
						const ctx = document.getElementById('mychart1');
						const canvasWidth = ctx.offsetWidth;
						const canvasHeight = ctx.offsetHeight;
						this.myEcharts.resize({
							width: canvasWidth,
							height: canvasHeight
						});
					}
				});
			},

问题六:echarts横坐标文字太长,需要换行展示 (使用formatter方法)

					xAxis: [{
						type: 'category',
						data: this.list.map(item => item.name),
						axisLabel: {
							align: 'center',
							color: '#303133',
							interval: 0,
							lineHeight: 20, // 控制行间距
							formatter: function(value) {
								let str = '';
								const num = 2;
								const valLength = value.length;
								const rowNum = Math.ceil(valLength / num);
								for (let i = 0; i < rowNum; i++) {
									const start = i * num;
									const temp = value.substring(start, start + num);
									str += temp + '\n';
								}
								return str.trim();
							}
						},
						axisLine: {
							lineStyle: {
								type: 'dashed',
								color: '#dcdfe6'
							}
						},
						axisTick: {
							alignWithLabel: true,
							show: false
						},

					}],

四:完整代码(uniapp框架有兴趣的朋友可以拿去试试,记得改接口),有帮助的话,双击、收藏不迷路

<template>
	<view class="card">
		<view class="card-title">
			<view class="card-title-left">
				<view class="line"></view>
				<view>你的标题</view>
			</view>
			<view class="card-title-right">
				<view>
					<u-picker @cancel="cancel" @confirm="confirm" :show="show" :columns="columns"></u-picker>
					<view @click="show = true" class="year-container">
						<text class="year-text">{{ year }}</text>
						<u-icon v-show="!show" name="arrow-down-fill" size="20" color="#1890ff" class="year-icon"></u-icon>
						<u-icon v-show="show" name="arrow-up-fill" size="20" color="#1890ff" class="year-icon"></u-icon>
					</view>
				</view>
			</view>
		</view>
		<view class="card-body">
			<!-- ECharts 容器 -->
			<view class="charts-box" @tap="handleTap">
				<canvas style="width:100%;height: 100%;zoom: 1 ;" id="mychart1" ref="echartsRef" canvas-id="mychart"
					:canvas-type="canvasType">
				</canvas>
			</view>
		</view>
	</view>
</template>

<script module="echartsScript" lang="renderjs">
	import * as echarts from 'echarts'
	import {
		getSelectDictionaryData
	} from '@/api/systemData/dictionary'
	import {
		investmentAmountInBusinessAreas,
		getProjectByBusinessArea
	} from '@/api/investmentOverview'

	export default {
		data() {
			return {
				show: false,
				canvasType: '2d',
				columns: this.generateYears(),
				year: new Date().getFullYear(),
				myEcharts: null,
				list: [],
				businessareaOptions: [],
				maxValue: 100,
				title: '',
				loading: false,
				projectList: []
			};
		},
		async mounted() {
			await Promise.allSettled([
				this.getDicData('SELECT', 'businessarea', {
					dicCode: 'BusinessArea'
				})
			]);
			await this.getData();
			this.$nextTick(() => {
				this.init();
			});
			window.addEventListener('resize', this.handleResize);
		},
		beforeDestroy() {
			window.removeEventListener('resize', this.handleResize);
		},
		methods: {
			handleTap(event) {
				this.onClick(event);
			},
			onClick(event) {
				const query = uni.createSelectorQuery().in(this);
				query.select('#mychart1').boundingClientRect((rect) => {
					if (rect) {
						const x = event.detail.x - rect.left; // 修正为相对于画布的 X 坐标
						const y = event.detail.y - rect.top; // 修正为相对于画布的 Y 坐标

						const xIndex = this.myEcharts.convertFromPixel({
							seriesIndex: 0
						}, [x, y])[0];

						this.myEcharts.dispatchAction({
							type: 'showTip',
							seriesIndex: 0,
							dataIndex: xIndex
						});
					}
				}).exec();
			},
			async getDicData(tag, prop, extra) {
				if (tag === 'SELECT' && !extra.options) {
					const res = await getSelectDictionaryData(extra.dicCode);
					const list = res.data;
					const options = prop + 'Options';
					this[options] = list.map(v => ({
						value: +v.enCode,
						label: v.fullName
					}));
				}
			},
			init() {
				const ctx = document.getElementById('mychart1');
				const canvasWidth = ctx.offsetWidth;
				const canvasHeight = ctx.offsetHeight;

				this.myEcharts = echarts.init(ctx, null, {
					width: canvasWidth,
					height: canvasHeight,
					renderer: 'svg'
				});
				const option = {
					title: {
						text: '',
						left: 'center',
						textStyle: {
							color: '#222',
							fontWeight: 500,
							fontSize: 24
						}
					},
					tooltip: {
						trigger: 'axis',
						axisPointer: {
							type: 'shadow'
						},
						borderWidth: 0,
						backgroundColor: 'rgba(0,0,0,0.5)',
						textStyle: {
							color: '#ffffff'
						},
						// enterable: true,
						position: function(point, params, dom, rect, size) {
							// point 是鼠标位置,params 是提示框数据
							var x = point[0]; // 获取鼠标的 x 坐标
							var y = point[1]; // 获取鼠标的 y 坐标

							// 提示框高度
							const tooltipHeight = size.contentSize[1];

							// 保持提示框在鼠标的上方显示,防止超出上边界
							if (y < tooltipHeight) {
								return [x + 10, y + 10];
							} else {
								return [x + 10, y - tooltipHeight - 10];
							}
						},
						confine: true, // 防止提示框超出画布边界
						formatter: function(params) {
							return params[0].name + ':' + params[0].value + '亿元';
						}

					},
					grid: {
						left: '40rpx',
						right: '0rpx',
						bottom: '90rpx',
						top: '32rpx'
					},
					xAxis: [{
						type: 'category',
						data: this.list.map(item => item.name),
						axisLabel: {
							align: 'center',
							color: '#303133',
							interval: 0,
							lineHeight: 20, // 控制行间距
							formatter: function(value) {
								let str = '';
								const num = 2;
								const valLength = value.length;
								const rowNum = Math.ceil(valLength / num);
								for (let i = 0; i < rowNum; i++) {
									const start = i * num;
									const temp = value.substring(start, start + num);
									str += temp + '\n';
								}
								return str.trim();
							}
						},
						axisLine: {
							lineStyle: {
								type: 'dashed',
								color: '#dcdfe6'
							}
						},
						axisTick: {
							alignWithLabel: true,
							show: false
						},

					}],
					yAxis: [{
						name: '亿元',
						type: 'value',
						min: 0,
						max: this.maxValue,
						interval: this.maxValue / 4,
						axisLine: {
							show: false
						},
						axisLabel: {
							// fontSize: '24rpx'
						},
						splitLine: {
							lineStyle: {
								type: 'dashed',
								color: '#dcdfe6'
							}
						},
						axisTick: {
							show: false
						}
					}],
					series: [{
						type: 'bar',
						itemStyle: {
							color: '#1890ff'
						},
						// barWidth: Math.max(36, 300 / this.list.length),
						barWidth: 20,
						data: this.list
					}],
					animationEasing: 'elasticOut'
				};
				this.myEcharts.setOption(option);
			},
			handleResize() {
				this.$nextTick(() => {
					if (this.myEcharts) {
						const ctx = document.getElementById('mychart1');
						const canvasWidth = ctx.offsetWidth;
						const canvasHeight = ctx.offsetHeight;
						this.myEcharts.resize({
							width: canvasWidth,
							height: canvasHeight
						});
					}
				});
			},
			async getData() {
				let res = await investmentAmountInBusinessAreas({
					year: this.year
				});
				if (res.code == 200) {
					this.list = [];
					this.businessareaOptions.forEach(item => {
						const options = res.data.find(v => v.businessarea == item.value);
						this.list.push({
							name: item.label,
							value: options?.deliveryamount || 0,
							projectcount: options?.projectcount || 0,
							enCode: item.value
						});
					});
					const maxCount = Math.ceil(Math.max(...this.list.map(item => item.value)) || 100);
					this.maxValue = Math.ceil(maxCount / Math.pow(10, String(maxCount).length - 1)) * Math.pow(10, String(
						maxCount).length - 1);
				}
			},
			confirm(arr) {
				this.year = arr.value[0];
				this.show = false;
				this.getData();
			},
			cancel() {
				this.show = false;
			},
			generateYears() {
				const year = new Date().getFullYear();
				const years = Array.from({
					length: 10
				}, (_, i) => year - i);
				years.push('全部');
				return [years];
			},
		}
	};
</script>




<style scoped lang="scss">
	.card {
		width: 100%;
		height: 540rpx;
		display: flex;
		flex-direction: column;
		border-radius: 12rpx;
		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
		background-color: #fff;
		overflow: hidden;
		z-index: 10;

		.card-title {
			display: flex;
			justify-content: space-between;
			padding: 30rpx;
			border-bottom: 1rpx solid #ccc;

			.line {
				width: 8rpx;
				height: 32rpx;
				background-color: #1890ff;
				margin-right: 12rpx;
				border-radius: 20rpx;
			}

			.card-title-left {
				display: flex;
				align-items: center;
			}

			.year-container {
				display: flex;
				align-items: center;
				white-space: nowrap;
				cursor: pointer;
			}

			.year-text {
				font-size: 28rpx;
				color: #000;
				margin-right: 10rpx;
			}

			.year-icon {
				vertical-align: middle;
			}
		}

		.card-body {
			// width: 100%;
			height: 100%;
			padding: 28rpx 20rpx;

			/* 请根据实际需求修改父元素尺寸,组件自动识别宽高 */

		}
	}

	.charts-box {
		width: 100%;
		height: 600rpx;
	}
</style>

### 如何在 UniApp使用 ECharts 实现移动端数据可视化 #### 安装与配置 为了使 EChartsUniApp 项目中正常工作,需先安装适用于 UniAppECharts 插件[^1]。对于仅限 H5 端的应用场景,可以直接引入传统的 ECharts 库;然而,在开发面向多平台的小程序时,则建议采用专门针对小程序优化过的版本或是像 uCharts 这样的替代方案[^2]。 #### 初始化图表容器 创建一个用于容纳图表的 HTML 结构,并为其指定唯一的 ID 属性以便后续 JavaScript 可以找到该元素并初始化图表实例: ```html <view id="main" style="width: 100%; height:400px;"></view> ``` #### 加载 ECharts 并绘制图形 接下来编写一段简单的脚本代码来加载 ECharts 库以及定义要呈现的具体图表类型及其样式设置。这里给出了一段基础的例子说明如何利用 `onReady` 生命周期钩子函数完成这一过程: ```javascript import * as echarts from 'echarts'; export default { mounted() { this.initChart(); }, methods: { initChart() { const chartDom = document.getElementById('main'); let myChart = echarts.init(chartDom); var option; option = { title: { text: 'ECharts Example' }, tooltip: {}, xAxis: { data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }; option && myChart.setOption(option); window.addEventListener("resize", function () { setTimeout(() => {myChart.resize()}, 100); // 延迟执行 resize 方法解决部分设备上可能出现的问题 }); } } } ``` 这段代码展示了如何在一个 Vue 组件内嵌入 ECharts 图表,并处理了当视窗大小发生变化时重新调整图表尺寸的需求[^4]。 #### 解决常见问题 有时可能会遇到一些特定于某些环境下的 bug ,比如工具提示 (tooltip) 功能可能不会按预期工作。对此类问题的一个潜在修复办法就是确保父级组件不是通过 `v-show` 来控制可见性的,而是改用 `v-if` 或者保证其 CSS 显示属性并非 "none"[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值