一、效果图
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>