如何实现echarts动画效果?如何实现表格或多个垂直布局的柱状图自动滚动效果?如何解决tooltip位置超出屏幕问题,如何解决legend文字过长,布局错乱问题?如何处理饼图的中心图片永远居中?
本文将主要解决以上问题,如有错漏,请指正.
一、大屏动画效果
这里的动画效果主要指,tooltip自动轮播,排名的柱状图或表格自动滚动效果。
1.实现柱状图,折线图自动轮播效果。
以折线图为例,其他的类似。
①封装一个折线图组件
<template>
<div class="bar-line"
ref="chartRef"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
props: {
dataList: {
required: false,
type: Object,
},
unit: {
type: Array,
required: false,
},
},
watch: {
dataList(value, oldValue) {
this.currentOption = value;
if (this.animotion) {
this.clearTimeAnimation()
this.set_Animation()
} else {
this.setOption();
}
}
},
data() {
return {
currentChart: null,
time_Highlight: null,
currentOption:null,
//是否tooltip开启动画
animotion:true,
}
},
methods: {
isNullorEmpty(str) {
return (
str === '' ||
str == '--' ||
str === null ||
str === undefined ||
isNaN(str)
)
},
initChart(){
this.init();
let myChart=echarts.init(this.$refs.chartRef);
this.currentChart=myChart
if (this.animotion) {
this.clearTimeAnimation()
this.set_Animation()
} else {
this.setOption();
}
},
setOption() {
let myChart=this.currentChart
let option=this.currentOption
myChart.clear();
option &&myChart.setOption(option);
window.addEventListener('resize', () => {
myChart.resize();
myChart.setOption(option)
});
},
// 清除定时器
clearTimeAnimation() {
if (this.time_Highlight) {
clearInterval(this.time_Highlight)
}
},
set_Animation() {
// 获取当前this
var that = this;
var option=that.currentOption;
// 鼠标悬浮事件
that.currentChart.on('mouseover', (params) => {
// console.log('悬浮')
// 清除定时器
that.clearTimeAnimation();
that.currentChart.dispatchAction({
type: 'downplay',
seriesIndex: seriesIndex
});
that.currentChart.dispatchAction({
type: 'highlight',
seriesIndex: seriesIndex,
dataIndex: params.dataIndex
});
that.currentChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: params.dataIndex,
});
that.time_Highlight = null;
})
// 鼠标移出事件
that.currentChart.on('mouseout', () => {
// 取消高亮效果
that.currentChart.dispatchAction({
type: 'downplay',
seriesIndex: seriesIndex
});
that.clearTimeAnimation();
setHighlight()
})
// 当前 X 轴下标
var myChartIndex = 0;
// 拿到数组长度
var dataLen = option.series[0].data.length;
// 需要显示的系列的下标
var seriesIndex = option.series.map((item, index) => (index))
// 是否完成一轮动画
var isReDataZoom = false;
// 是否 Y 轴滚动缩放 Y轴缩放 需要增加 orient='vertical' 属性 Y轴0到1是向上的 需要1到0是向下滚动
var orientZoom = false;
if (orientZoom) {
//Y 最顶端开始
myChartIndex = dataLen - 1;
}
// 设置高亮和提示框
function setHighlight() {
option && that.currentChart.setOption(option);
that.time_Highlight = setInterval(() => {
// 取消高亮
that.currentChart.dispatchAction({
type: 'downplay',
seriesIndex: seriesIndex,
dataIndex: myChartIndex
});
// 取消提示框
that.currentChart.dispatchAction({
type: 'hideTip',
seriesIndex: 0,
dataIndex: myChartIndex
});
// 是否Y轴
if (orientZoom) {
myChartIndex = (myChartIndex - 1) % dataLen;
if (myChartIndex < 0) {
myChartIndex = dataLen;
// 同步
isReDataZoom = true;
}
} else {
myChartIndex = (myChartIndex + 1) % dataLen;
if (myChartIndex === 0) {
// 同步
isReDataZoom = true;
}
}
// 高亮
that.currentChart.dispatchAction({
type: 'highlight',
seriesIndex: seriesIndex,
dataIndex: myChartIndex
});
// 显示提示框
that.currentChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: myChartIndex
});
}, 1950)
}
setHighlight()
},
init() {
let that=this;
let unit=this.unit;
let xData = this.dataList.xData
const seriesList = []
this.dataList.seriesList.forEach((item, index) => {
let series = {}
series = {
name: item.name,
type: 'line',
symbol: 'emptyCircle',
symbolSize: 4, //标记的大小
data: item.yData,
yAxisIndex: item.yIndex === 1 ? item.yIndex : 0,
connectNulls: item.connectNulls ? item.connectNulls : false,
color: item.color ? item.color : 'skyblue',
}
if (item.type == 'line' && item.isAreaColor) {
series.lineStyle = {
width: 2,
}
series.areaStyle = {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: item.start, // 0% 处的颜色
},
{
offset: 1,
color: item.end, // 100% 处的颜色
},
],
},
}
}
seriesList.push(series)
})
let option = {
grid: {
left: '5%',
right: '5%',
top: '15%',
bottom: '12%',
containLabel: true,
},
legend: {
show: true,
y: '0%',
itemWidth: 15,
itemHeight: 10,
lineStyle: {
type: 'solid',
},
textStyle: {
color: '#eee',
fontSize: '11',
},
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
let tip = params[0].name;
for (let i = 0; i < params.length; i++)
{
const unit2 = seriesList[i].yAxisIndex === 1 ? unit[1] : unit[0];
if( that.isNullorEmpty(params[i].value)){
tip +=
'<br/>' + params[i].marker + params[i].seriesName + ':' +'--' + unit2;
}else{
tip +=
'<br/>' + params[i].marker + params[i].seriesName + ':' + Number(params[i].value).toFixed(2) + unit2;
}
}
return tip;
},
},
xAxis: [
{
type: 'category',
axisLabel: {
textStyle: {
color: '#fff',
fontSize: '12',
},
interval: 0,
},
axisLine: {
show: true,
lineStyle: {
color: 'rgb(41,111,198)',
},
},
axisTick: {
show: false,
},
data: xData,
},
],
yAxis: [
{
name: unit[0],
nameTextStyle: {
fontSize: '12' /* 14/192 */,
color: '#D9EDFF',
},
type: 'value',
axisTick: {
show: false,
},
splitLine: {
show: false,
lineStyle: { type: 'solid', color: 'rgb(41,111,198)' },
},
axisLabel: {
textStyle: {
color: ' rgb(125, 170, 197)',
},
fontSize: '12' /* 14/192 */,
},
axisLine: {
show: true,
lineStyle: {
color: 'rgb(41,111,198)',
},
},
},
{
nameTextStyle: {
fontSize: '12' /* 14/192 */,
color: '#D9EDFF',
},
name: unit[1],
show: this.dataList.showDoubleY,
type: 'value',
axisTick: {
show: false,
},
splitLine: {
show: false,
lineStyle: { type: 'solid', color: 'rgb(41,111,198)' },
},
axisLabel: {
textStyle: {
color: ' rgb(125, 170, 197)',
},
fontSize: '12' /* 14/192 */,
},
axisLine: {
show: true,
lineStyle: {
color: 'rgb(41,111,198)',
},
},
},
],
series: seriesList,
}
this.currentOption=option;
},
},
mounted() {
if (this.dataList.xData) {
this.initChart();
}
},
beforeDestroy() {
// 清除定时器
this.clearTimeAnimation()
this.currentChart && this.currentChart.dispose()
this.currentChart = null;
}
}
</script>
<style scoped>
.bar-line {
width: 100%;
height: 100%;
}
</style>
②使用折线图
<CategoryChart :key="lineChart"
:data-list="line"
:unit="['%']"
/>
lineChart: {
xData: ['01','02','03','04','05','06'],
seriesList: [
{
type: 'line',
name: 'series1',
isAreaColor: true,
yIndex: 0,
yData: [],
color: '#28CCDC',
start: 'rgba(40,204,220,0.6)',
end: 'rgba(40,204,220,0.1)',
},
{
type: 'line',
isAreaColor: true,
name: 'series2',
yIndex: 0,
yData: [],
color: '#E9D41E',
start: 'rgba(233,212,30,0.6)',
end: 'rgba(233,212,30,0.1)',
},
],
showDoubleY: false,
},
2.实现饼图tooltip效果
同下方饼图全屏后布局错乱一起讲解。
3.实现自动滚动动画效果
(1)表格数据自动滚动
使用插件vue3-seamless-scroll-无缝滚动或vue-seamless-scroll
参考:
vue3实现无缝滚动列表-vue3-seamless-scroll-CSDN博客
(2)当排名使用多个柱状图实现,
注意:以下方案未实现无缝滚动.
(这里因为使用一个柱状图的多个series无法实现设计图效果,所以使用多个柱状图的单个series实现设计图效果)
vue3-seamless-scroll的原理是再复制一份一样的在原数据下放,而echarts是不可以用相同的id的,所以复制的柱状图不能正确显示.
(1)搭建结构
<div class="content-box"
id="scroll-containers">
<div class="scroll-container">
<BarSort :key="item.id"
:data-list="item.data"
:maxArr="maxArr"
:id="item.id"
v-for="(item, index) in list" />
</div>
</div>
.content-box {
width: 100%;
position: relative;
overflow: hidden;
.scroll-container {
display: flex;
flex-direction: column;
}
}
(2)分装组件
<template>
<div class="bar-line"
ref="chartRef"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
props: {
dataList: {
required: false,
type: Array,
},
maxArr: {
required: false,
type: Array,
},
id: {
required: false,
type: String,
}
},
watch: {
dataList: {
handler: function(newValue,oldValue) {
this.init();
}
}
},
data() {
return {
currentChart: null,
}
},
methods: {
isNullorEmpty(str) {
return (
str === '' ||
str == '--' ||
str === null ||
str === undefined ||
isNaN(str)
)
},
init() {
let id=this.id;
let datas = this.dataList;
let xData = [this.id + this.dataList[0].name]
// let maxArr = new Array(datas.length).fill(datas[0].value * 2);
let maxArr = this.maxArr;
let valueArr = [];
for (let i = 0; i < datas.length; i++)
{
let obj = {
value: datas[0].value,
name: datas[i].value,
}
valueArr.push(obj)
}
const option = {
title: {
text: "",
show: false
},
tooltip: {
show: true,
position: [10, -20],
formatter: function (params) {
if (params.seriesIndex == 0)
{
return params.name + ':' + params.data['value'] + ''
}
},
},
grid: {
left: '10%',
right: '10%',
bottom: '2%',
top: '20%',
// containLabel: true,
},
xAxis: {
show: false
},
yAxis: [
{
type: 'category',
inverse: true,
nameGap: 16,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
interval: 0,
align: 'left',
margin: 30,
textStyle: {
color: '#fff',
align: 'left',
fontSize: 10
},
rich: {
a: {
padding: [0, 0, 20, 10]
},
a1: {
color: '#E93F3F',
backgroundColor: 'rgba(233, 63, 63, 0.20)',
shadowColor: '#E93F3F',
borderColor: '#E93F3F',
borderWidth: 0.5,
width: 20,
height: 20,
align: 'center',
borderRadius: 2
},
a2: {
color: '#D7743E',
backgroundColor: 'rgba(215,116,62,0.2)',
shadowColor: '#D7743E',
borderColor: '#D7743E',
borderWidth: 0.5,
width: 20,
height: 20,
align: 'center',
borderRadius: 2
},
a3: {
color: '#D7BC3E',
backgroundColor: 'rgba(215,188,62,0.3)',
shadowColor: '#D7BC3E',
borderColor: '#D7BC3E',
borderWidth: 0.5,
width: 20,
height: 20,
align: 'center',
borderRadius: 2
},
b: {
color: '#A5C6FF',
backgroundColor: 'rgba(32, 109, 241, 0.2)',
shadowColor: '#3374ba',
borderColor: '#3374ba',
borderWidth: 0.5,
width: 20,
height: 20,
align: 'center',
borderRadius: 2
}
},
formatter: function (params) {
if (parseInt(params.slice(0, 1)) > 2)
{
return [
'{b|' + (parseInt(params.slice(0, 1)) + 1) + '}' + ' ' + '{a|' + params.slice(1) + '}'
].join('\n')
} else
if (parseInt(params.slice(0, 1)) == 0)
{
return [
'{a1|' + (parseInt(params.slice(0, 1)) + 1) + '}' + ' ' + '{a|' + params.slice(1) + '}'
].join('\n')
}
if (parseInt(params.slice(0, 1)) == 1)
{
return [
'{a2|' + (parseInt(params.slice(0, 1)) + 1) + '}' + ' ' + '{a|' + params.slice(1) + '}'
].join('\n')
}
if (parseInt(params.slice(0, 1)) == 2)
{
return [
'{a3|' + (parseInt(params.slice(0, 1)) + 1) + '}' + ' ' + '{a|' + params.slice(1) + '}'
].join('\n')
}
}
},
data: xData
},
// {
// type: 'category',
// inverse: true,
// axisTick: 'none',
// axisLine: 'none',
// axisLabel: {
// show: true,
// fontSize: 12,
// color: '#fff',
// inside: true,
// formatter: '{a|{value}}',
// rich: {
// a: {
// padding: [0, 0, 30, 0]
// }
// }
// },
// data: datas.map((item) => item.value),
// },
],
series: [
{
name: 'barSer',
type: 'bar',
roam: false,
visualMap: false,
zlevel: 2,
barWidth:8,
barMaxWidth: 8,
barGap: 0,
itemStyle: {
normal: {
color: function (params) {
var colorList = [
{
colorStops: [{
offset: 0,
color: 'rgba(32, 109, 242, 0.4)' // 0% 处的颜色
}, {
offset: 1,
color: 'rgba(32, 109, 242, 0.99)' // 100% 处的颜色
}]
},
{
colorStops: [{
offset: 0,
color: 'rgba(233, 63, 63, 0.4)' // 0% 处的颜色
}, {
offset: 1,
color: 'rgba(233, 63, 63, 0.99)' // 100% 处的颜色
}]
}, {
colorStops: [{
offset: 0,
color: 'rgba(215, 116, 62, 0.4)' // 0% 处的颜色
}, {
offset: 1,
color: 'rgba(215, 116, 62, 0.99)' // 100% 处的颜色
}]
},
{
colorStops: [{
offset: 0,
color: 'rgba(215, 189, 94, 0.4)' // 0% 处的颜色
}, {
offset: 1,
color: 'rgba(215, 189, 94, 0.99)' // 100% 处的颜色
}]
}
];
if (id > 2)
{
return colorList[0]
}
else
{
if (id == 0)
{
return colorList[1]
}
if (id == 1)
{
return colorList[2]
}
if (id == 2)
{
return colorList[3]
}
}
},
barBorderRadius: 15
}
},
data: datas
},
{
name: '背景',
type: 'bar',
barWidth: 8,
barGap: '-100%',
itemStyle: {
color: 'rgba(18, 78, 133, 0.55)',
barBorderRadius: 15
},
tooltip: {
show: false
},
data: maxArr,
},
{
name: '值',
type: 'bar',
barWidth: 8,
barGap: '-100%',
data: maxArr,
itemStyle: {
normal: {
color: 'transparent',
},
},
label: {
normal: {
color: '#fff',
show: true,
position: ['94%', '-15px'],
textStyle: {
fontSize: 12,
},
formatter: function (params) {
return valueArr[0].name;
},
},
},
},
],
}
let myChart = echarts.init(this.$refs.chartRef)
myChart.clear()
option && myChart.setOption(option)
window.addEventListener('resize', () => {
myChart.resize()
myChart.setOption(option)
})
},
},
mounted() {
this.init()
},
}
</script>
<style scoped>
.bar-line {
width: calc(100% - 20px);
height: 100%;
/* height: 16%; */
margin: 8px;
height:40px;
background: rgb(18 78 133 / 20%);
border: 1px solid #113E73;
}
</style>
(3)分装滚动函数
initScroll() {
let container = document.getElementById('scroll-containers')
if (this.scrollInterval) {
container.scrollTop = 0
clearInterval(this.scrollInterval)
this.scrollInterval = null
}
this.scrollInterval = setInterval(() => {
container.scrollTop += 2 // 向上滚动的速度
// 获取指定区域的高度
const elementHeight = container.clientHeight
// 获取指定区域内文档内容的总高度
const contentHeight = container.scrollHeight
// 获取指定区域的滚动位置
const scrollPosition = container.scrollTop
// 计算滚动条距离底部的距离
const distanceToBottom = contentHeight - elementHeight - scrollPosition
if (distanceToBottom === 0) {
container.scrollTop = 0
}
}, 50) // 滚动间隔,单位毫秒
},
(4)给柱状图赋值
maxArr: [400],
list: [
{
id: '0',
data: [
{
name: 'xxx1',
value: 0,
},
],
},
{
id: '1',
data: [
{
name: 'xxx2',
value: 0,
},
],
},
{
id: '2',
data: [
{
name: 'xxx3',
value: 0,
},
],
},
{
id: '3',
data: [
{
name: 'xxx4',
value: 0,
},
],
},
{
id: '4',
data: [
{
name: 'xxx5',
value: 0,
},
],
},
],
至此自动滚动效果完成.
二、解决全屏下饼图使用的graphic图片布局偏移问题
当设计图的布局是如下图所示,如何保证图片永远在饼图居中位置?
①拆分盒子模型为左右两部分,左右两侧为两个饼图,左侧不显示legend,右侧不显示饼图.
<div class="content-box">
<div class="pie-left-box">
<pieChart1 :key="rightCenterList"
:data-list="rightCenterList"
:pieParams="pieParams2"
:unit="['%']"
:changeParams="changeParams2"
:legendIndexOver="legendIndexOver2"
:legendIndexOut="legendIndexOut2"
:legendIndexOverFlag="legendIndexOverFlag2"
:legendIndexOutFlag="legendIndexOutFlag2" />
</div>
<div class="pie-right-box">
<pieChart2 :key="rightCenterList"
:data-list="rightCenterList"
@changeLegendSelected="changeLegendSelected2"
:unit="['%']"
@legendMouseover="legendMouseover2"
@legendMouseout="legendMouseout2" />
</div>
</div>
②封装饼图组件-左侧饼图
(1)封装autoPlay函数,处理tooltip自动轮播
(2)封装 LineFeedLabel函数,处理legend过长问题
(3)tooltip中position函数,处理tooltip弹框被盒子盖住,或超出屏幕问题
<template>
<div class="bar-line"
ref="chartRef"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
props: {
dataList: {
required: false,
type: Object,
},
unit: {
required: false,
type: Array,
},
pieParams: {
required: false,
type: String,
},
changeParams: {
required: false,
type: Boolean,
},
legendIndexOver: {
required: false,
},
legendIndexOut: {
required: false,
},
legendIndexOverFlag: {
required: false,
type: Boolean,
},
legendIndexOutFlag: {
required: false,
type: Boolean,
}
},
watch: {
changeParams: {
handler (newVal, oldVal) {
if (newVal !== oldVal) {
this.handleChangeLegendSelected(this.pieParams)
}
},
},
legendIndexOverFlag: {
handler (newVal, oldVal) {
if (newVal !== oldVal) {
this.handleLegendMouseover(this.legendIndexOver)
}
},
},
legendIndexOutFlag: {
handler (newVal, oldVal) {
if (newVal !== oldVal) {
this.handleLegendMouseout(this.legendIndexOut)
}
},
},
},
data() {
return {
myChart: null,
timer:null,
}
},
methods: {
handleChangeLegendSelected (params) {
this.myChart.dispatchAction({
type: "legendToggleSelect",
// 图例名称
name: params,
});
},
handleLegendMouseover (legendIndex) {
this.myChart.dispatchAction({
type: "highlight",
seriesIndex: 0,// 这个是数据系列,有的一个echarts里面放了多组数据,本案例只有一组所以是0
dataIndex: legendIndex,// 第几个图例
});
this.myChart.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex: legendIndex,
});
},
handleLegendMouseout (legendIndex) {
this.myChart.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: legendIndex,
});
this.myChart.dispatchAction({
type: "hideTip",
seriesIndex: 0,
dataIndex: legendIndex,
});
},
//lengend的文字长度过长,换行
LineFeedLabel (data, length) {
//data 要处理的字符串
//length 每行显示长度
let word = ''
let num = Math.ceil(data.length / length) // 向上取整数
// 一行展示length个
if (num > 1)
{
for (let i = 1; i <= num; i++)
{
word += data.substr((i - 1) * length, length)
if (i < num)
{
word += '\n'
}
}
} else
{
word += data.substr(0, length)
}
return word
},
isNullorEmpty(str) {
return (
str === '' ||
str == '--' ||
str === null ||
str === undefined ||
isNaN(str)
)
},
// 自动播放函数
autoPlay() {
let index = 0;
let that=this;
if(this.timer){
clearInterval(this.timer)
this.timer=null;
}
this.timer = setInterval(function () {
that.myChart.dispatchAction({
type: 'hideTip',
seriesIndex: 0,
dataIndex: index,
});
// 显示提示框
that.myChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: index,
});
// 取消高亮指定的数据图形
that.myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: index === 0 ? that.dataList.yData.length-1 : index - 1,
});
that.myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: index,
});
index++;
if (index > that.dataList.yData.length-1 ) {
index = 0;
}
}, 3000);
},
generateRandomColor(item) {
// 生成三个随机数作为 RGB 值
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
// 组装成颜色字符串
var color = 'rgba(' + r + ',' + g + ',' + b + ','+item+')';
return color;
},
init() {
let dataList = this.dataList;
let unit = this.unit;
let chartData = this.dataList.yData;
let sum = chartData.reduce((per, cur) => per + cur.value, 0)
let colors=this.dataList.colorList;
if(Number(chartData.length)>Number(colors.length)){
for(let i=colors.length-1;i<chartData.length;i++){
colors.push(this.generateRandomColor(0.5))
}
}
let seriesList = [{
name: this.dataList.name,
type: 'pie',
radius: this.dataList.radius,
center: this.dataList.center,
color: colors,
hoverAnimation: true,
label: {
show: false,
},
labelLine: {
show: false
},
data: chartData,
},
];
let optionsImg = [];
if (this.dataList.imgArr.length > 0)
{
for (let i = 0; i < this.dataList.imgArr.length; i++)
{
let item = this.dataList.imgArr[i]
let bgPatternImg = new Image();
bgPatternImg.src = item.imgSrc
let obj = {
type: 'image',
// z: 3,
style: {
image: bgPatternImg,
width: item.width,
height: item.height,
},
left: item.left,
top: item.right,
}
optionsImg.push(obj)
}
}
let option = {
title: {
text:this.dataList.title,
left:'center',
subtext:this.dataList.subtext,
textStyle:{
color:'#fff',
fontSize:20,
padding:[0,0],
align:'center'
},
subtextStyle: {
fontSize: 13,
color: 'RGBA(163, 195, 215, 1)',
align:'center'
},
x: 'center',
y: '40%',
},
grid: { left: '2%', right: '2%', top: '15%', bottom: '5%', containLabel: true },
legend: {
show: false
},
graphic: {
elements: optionsImg
},
tooltip: {
trigger: 'item',
// 下面的函数,用于tip可以完整显示,不用被盒子挡住
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是:以外层div的左上角那一点为原点,x轴向右,y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX)
{
x = 5;
} else
{
// 左边放的下
x = pointX - boxWidth;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight > pointY)
{
y = 5;
} else
{
// 上边放得下
y = pointY - boxHeight;
}
return [x, y];
}, // 设置显示位置
// axisPointer: { type: 'shadow' },
formatter (params) {
let tip = dataList.name;
const unitShow = seriesList[0].yAxisIndex === 1 ? unit[1] : unit[0]
tip
+= `<br/>${params.marker}${params.name}:${params.value && params.value != '--' ? Number(params.value).toFixed(2) : params.value}${unitShow}`
return tip
},
},
series: seriesList,
}
let myChart2 = echarts.init(this.$refs.chartRef)
myChart2.clear()
option && myChart2.setOption(option)
window.addEventListener('resize', () => {
myChart2.resize()
myChart2.setOption(option)
})
this.myChart=myChart2
},
},
mounted() {
this.init()
this.autoPlay()
},
beforeDestroy() {
// 清除定时器
if(this.timer){
clearInterval(this.timer)
this.timer=null;
}
}
}
</script>
<style scoped>
.bar-line {
width: 100%;
height: 100%;
}
</style>
③封装饼图组件-右侧饼图
(1)radius为0不显示饼图
<template>
<div class="lineContent">
<div class="unit" :style="{ right: `${rightDistance}`, top: `${topDistance}` }">万tce</div>
<div class="bar-line"
ref="chartRef"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
props: {
dataList: {
required: false,
type: Object,
},
},
data() {
return {
myChart: null,
rightDistance: '10%', // 初始左侧距离,可以根据需要设置
topDistance: '20%', // 初始顶部距离,可以根据需要设置
}
},
methods: {
handleLegendClick(params) {
this.$emit('changeLegendSelected', params.name)
},
handleLegendMouseover(event) {
// 获取鼠标现在所在的位置
const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
// 因为不仅仅是图例可以触发鼠标经过事件,不是图例的没用index,所以要过滤一下
if (legendDataIndex != undefined && legendDataIndex >= 0) {
this.$emit('legendMouseover', legendDataIndex)
}
},
handleLegendMouseout(event) {
// 有的鼠标移出触发的事件没用parent属性所以要加?为可选的
if (event.topTarget?.parent != undefined) {
const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
// 该监听器正在监听一个`echarts 事件`。
if (legendDataIndex != undefined && legendDataIndex >= 0) {
this.$emit('legendMouseout', legendDataIndex)
}
}
},
//lengend的文字长度过长,换行
LineFeedLabel(data, length) {
//data 要处理的字符串
//length 每行显示长度
let word = ''
let num = Math.ceil(data.length / length) // 向上取整数
// 一行展示length个
if (num > 1) {
for (let i = 1; i <= num; i++) {
word += data.substr((i - 1) * length, length)
if (i < num) {
word += '\n'
}
}
} else {
word += data.substr(0, length)
}
return word
},
isNullorEmpty(str) {
return (
str === '' ||
str == '--' ||
str === null ||
str === undefined ||
isNaN(str)
)
},
generateRandomColor(item) {
// 生成三个随机数作为 RGB 值
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
// 组装成颜色字符串
var color = 'rgba(' + r + ',' + g + ',' + b + ','+item+')';
return color;
},
init() {
let chartData = this.dataList.yData
let sum = chartData.reduce((per, cur) => per + cur.value, 0)
let colors=this.dataList.colorList;
if(Number(chartData.length)>Number(colors.length)){
for(let i=colors.length-1;i<chartData.length;i++){
colors.push(this.generateRandomColor(0.5))
}
}
let seriesList = [
{
name: '',
type: 'pie',
radius: '0%',
center: ['50%', '50%'],
color: colors,
hoverAnimation: true,
label: {
show: false,
},
labelLine: {
show: false,
},
data: chartData,
},
]
const option = {
grid: {
left: '2%',
right: '2%',
top: '15%',
bottom: '5%',
containLabel: true,
},
legend: {
type: 'scroll',
orient: 'vertical',
align: 'left',
// left: '63%',
left: 'center',
top: 'middle',
itemHeight: 12,
itemWidth: 12,
lineStyle: {
type: 'solid',
},
textStyle: {
color: '#ffffff',
},
textStyle: {
// 个
color: '#D8DDE3',
rich: {
name: {
verticalAlign: 'right',
align: 'left',
// width: 80,
width: this.dataList.legendWidth,
fontSize: 14,
color: 'rgba(196, 233, 255, 1)',
},
number: {
padding: [0, 0, 0, 8],
color: '#fff',
fontWeight: 700,
},
},
borderWidth: 53, // 间距的宽度
},
formatter: (name) => {
const item = chartData.find((i) => {
return i.name === name
})
if (name) {
name = this.LineFeedLabel(name, this.dataList.legendSlice)
}
return '{name|' + name + '}' + '{number|' + item.value + '}'
},
pageIconColor: 'rgba(188, 224, 246, 1)',
pageTextStyle: {
color: 'rgba(188, 224, 246, 1)',
},
pageIconColor: 'rgba(188, 224, 246, 1)',
pageTextStyle: {
color: 'rgba(188, 224, 246, 1)',
},
},
tooltip: {
show: false,
},
series: seriesList,
}
let myChart2 = echarts.init(this.$refs.chartRef)
myChart2.clear()
option && myChart2.setOption(option)
window.addEventListener('resize', () => {
myChart2.resize()
myChart2.setOption(option)
})
//图例点击事件
myChart2.on('legendselectchanged', this.handleLegendClick)
//图例鼠标移入事件
myChart2.getZr().on('mouseover', this.handleLegendMouseover)
//图例鼠标移出事件
myChart2.getZr().on('mouseout', this.handleLegendMouseout)
this.myChart = myChart2
},
},
mounted() {
this.rightDistance=this.dataList?.rightDistance,
this.topDistance=this.dataList?.topDistance
this.init()
},
beforeUnmount() {
this.myChart.off('legendselectchanged', this.handleLegendClick)
this.myChart.getZr().off('mouseover', this.handleLegendMouseover)
this.myChart.getZr().off('mouseout', this.handleLegendMouseout)
this.myChart.dispose() // 销毁图表对象
this.myChart = null // 清空对图表对象的引用
},
}
</script>
<style scoped>
.lineContent{
width: 100%;
height: 100%;
position: relative;
}
.unit{
position: absolute;
right:17%;
top:15%;
color:RGBA(163, 195, 215, 1);
font-size:12px;
}
.bar-line {
width: 100%;
height: 100%;
}
</style>
⑤为饼图赋值.
rightCenterList: {
yData: [
{
value: 0,
name: 'xx1',
},
{
value: 0,
name: 'xx2',
},
{
value: 0,
name: 'xx3',
},
{
value: 0,
name: 'xx4',
},
],
name: 'xxxseries名称',
title: 'xxx主标题',
subtext: 'xxx副标题',
radius: ['50%', '70%'],
center: ['50%', '50%'],
legendWidth: 120,
legendSlice: 10,
imgArr: [
{
imgSrc: '/screen/环图背景.png',
width: 180,
height: 200,
left: 'center',
right: 'center',
},
],
colorList: [
'rgba(74, 182, 76, 1)',
'rgba(40, 174, 232, 1)',
'rgba(215, 189, 94, 1)',
'rgba(161, 200, 227, 1)',
],
},
⑥左右饼图进行关联,鼠标点击或划入legend的某项,左侧饼图tooltip要定位到该项
(1)右侧饼图echarts监听事件,并传给父亲
//图例点击事件
myChart2.on('legendselectchanged', this.handleLegendClick)
//图例鼠标移入事件
myChart2.getZr().on('mouseover', this.handleLegendMouseover)
//图例鼠标移出事件
myChart2.getZr().on('mouseout', this.handleLegendMouseout)
handleLegendClick(params) {
this.$emit('changeLegendSelected', params.name)
},
handleLegendMouseover(event) {
// 获取鼠标现在所在的位置
const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
// 因为不仅仅是图例可以触发鼠标经过事件,不是图例的没用index,所以要过滤一下
if (legendDataIndex != undefined && legendDataIndex >= 0) {
this.$emit('legendMouseover', legendDataIndex)
}
},
handleLegendMouseout(event) {
// 有的鼠标移出触发的事件没用parent属性所以要加?为可选的
if (event.topTarget?.parent != undefined) {
const legendDataIndex = event.topTarget.parent.__legendDataIndex // 每个legend的index
// 该监听器正在监听一个`echarts 事件`。
if (legendDataIndex != undefined && legendDataIndex >= 0) {
this.$emit('legendMouseout', legendDataIndex)
}
}
},
(2)父盒子接受子盒子传递的数据,传给左侧饼图
data() {
return {
pieParams: '',
changeParams: false,
//饼图 legend与饼图的交互
legendIndexOver: '',
legendIndexOut: '',
legendIndexOverFlag: false,
legendIndexOutFlag: false,
}
}
changeLegendSelected(params) {
this.pieParams = params
this.changeParams = !this.changeParams
},
legendMouseover(legendIndex) {
this.legendIndexOver = legendIndex
this.legendIndexOverFlag = !this.legendIndexOverFlag
},
legendMouseout(legendIndex) {
this.legendIndexOut = legendIndex
this.legendIndexOutFlag = !this.legendIndexOutFlag
},
(3)左侧饼图监听参数改变触发legend事件
watch: {
changeParams: {
handler (newVal, oldVal) {
if (newVal !== oldVal) {
this.handleChangeLegendSelected(this.pieParams)
}
},
},
legendIndexOverFlag: {
handler (newVal, oldVal) {
if (newVal !== oldVal) {
this.handleLegendMouseover(this.legendIndexOver)
}
},
},
legendIndexOutFlag: {
handler (newVal, oldVal) {
if (newVal !== oldVal) {
this.handleLegendMouseout(this.legendIndexOut)
}
},
},
},
handleChangeLegendSelected (params) {
this.myChart.dispatchAction({
type: "legendToggleSelect",
// 图例名称
name: params,
});
},
handleLegendMouseover (legendIndex) {
this.myChart.dispatchAction({
type: "highlight",
seriesIndex: 0,// 这个是数据系列,有的一个echarts里面放了多组数据,本案例只有一组所以是0
dataIndex: legendIndex,// 第几个图例
});
this.myChart.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex: legendIndex,
});
},
handleLegendMouseout (legendIndex) {
this.myChart.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: legendIndex,
});
this.myChart.dispatchAction({
type: "hideTip",
seriesIndex: 0,
dataIndex: legendIndex,
});
},
至此完成饼图的graphic全屏布局会错乱问题
参考链接
echarts组件封装-动画效果_chart组件封装 动态id-CSDN博客文章浏览阅读794次。echarts组件封装-动画效果。_chart组件封装 动态idhttps://blog.csdn.net/weixin_46253839/article/details/126015918?fromshare=blogdetail&sharetype=blogdetail&sharerId=126015918&sharerefer=PC&sharesource=Primary_Insist&sharefrom=from_link
vue3实现无缝滚动列表-vue3-seamless-scroll-CSDN博客文章浏览阅读2.5k次,点赞20次,收藏19次。vue3实现无缝滚动列表-vue3-seamless-scroll_vue3-seamless-scrollhttps://blog.csdn.net/weixin_48594833/article/details/139901578?fromshare=blogdetail&sharetype=blogdetail&sharerId=139901578&sharerefer=PC&sharesource=Primary_Insist&sharefrom=from_link