文章目录
- 概要
- 技术细节
- 效果
概要
需求需要实现图例移入显示描述说明
故实现自定义图例
技术细节
<template><div class="custom-legend"><divv-for="item in legends":key="item.name"class="legend-item":class="{ 'is-disabled': !isItemActive(item.name) }"@click="toggleLegend(item)"><spanclass="legend-marker":style="{ backgroundColor: item.color }"></span><a-tooltip><template #title><span>{{ item.tooltip || item.name }}</span></template><span>{{ item.name }}</span></a-tooltip></div></div>
</template><script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'const props = defineProps({chartInstance: {type: Object,default: () => null},customTooltips: {type: Object,default: () => ({})}
})const legends = ref([])
const activeMap = ref({})const isItemActive = computed(() => (name) => {return activeMap.value[name]
})const toggleLegend = (item) => {if (!props.chartInstance) returnconst name = item.nameconst newState = !activeMap.value[name]activeMap.value = { ...activeMap.value, [name]: newState }props.chartInstance.setOption({legend: {selected: activeMap.value}})
}const renderLegends = () => {if (!props.chartInstance) {legends.value = []return}const chart = props.chartInstanceconst options = chart.getOption()const { series, color: globalColors, legend } = optionsconst chartSelectedMap = legend?.[0]?.selected || {}activeMap.value = { ...chartSelectedMap }const legendItems = []series.forEach((seriesItem, seriesIndex) => {if (seriesItem.type === 'pie') {seriesItem.data.forEach((dataItem, dataIndex) => {const name = dataItem.nameconst color =dataItem.itemStyle?.color ||globalColors[dataIndex % globalColors.length] ||'#000'if (activeMap.value[name] === undefined) {activeMap.value[name] = true}legendItems.push({name,color,tooltip: props.customTooltips[name] || name})})} else {const name = seriesItem.nameconst color =seriesItem.itemStyle?.color ||globalColors[seriesIndex % globalColors.length] ||'#000'if (activeMap.value[name] === undefined) {activeMap.value[name] = true}legendItems.push({name,color,tooltip: props.customTooltips[name] || name})}})legends.value = legendItems
}const handleChartLegendChange = (params) => {activeMap.value = { ...params.selected }
}watch(() => props.chartInstance,(newVal) => {if (newVal) {renderLegends()} else {legends.value = []}},{ immediate: true }
)
</script><style scoped lang="scss">
.custom-legend {display: flex;flex-wrap: wrap;justify-content: center;gap: 12px;padding: 8px;.legend-item {display: flex;align-items: center;gap: 6px;cursor: pointer;transition: all 0.2s ease;.legend-marker {width: 14px;height: 14px;border-radius: 50%;transition: all 0.2s ease;}&.is-disabled {opacity: 0.6;.legend-marker {filter: grayscale(100%);transform: scale(0.9);}}&:hover:not(.is-disabled) {transform: translateY(-1px);opacity: 0.9;}}
}
</style>
<template><div class="container"><div class="chart" ref="lineChart"></div><div class="chart" ref="pieChart"></div><div class="legend1"><Legend :chartInstance="chartInstance1" /></div><div class="legend2"><Legend :chartInstance="chartInstance2" /></div></div>
</template>
<script setup>
import * as echarts from 'echarts'
import Legend from './components/Legend.vue'const lineChart = ref(null)
const pieChart = ref(null)const chartInstance1 = shallowRef(null)
const chartInstance2 = shallowRef(null)const _data = {line: {title: '折线图示例',categories: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],legend: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],series: [{name: '邮件营销',type: 'line',data: [120, 132, 101, 134, 90, 230, 210]},{name: '联盟广告',type: 'line',data: [220, 182, 191, 234, 290, 330, 310]},{name: '视频广告',type: 'line',data: [150, 232, 201, 154, 190, 330, 410]},{name: '直接访问',type: 'line',data: [320, 332, 301, 334, 390, 330, 320]},{name: '搜索引擎',type: 'line',data: [820, 932, 901, 934, 1290, 1330, 1320]}]},pie: {title: '饼图示例',legend: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'],series: [{ value: 335, type: 'pie', name: '直接访问' },{ value: 310, type: 'pie', name: '邮件营销' },{ value: 234, type: 'pie', name: '联盟广告' },{ value: 135, type: 'pie', name: '视频广告' },{ value: 1548, type: 'pie', name: '搜索引擎' }]}
}const onItemClick = (params) => {chartInstance1.value.dispatchAction({type: 'legendToggleSelect',name: params.name})
}const initChart = () => {const lineOption = {title: {text: _data.line.title},tooltip: {trigger: 'axis'},legend: {show: false},grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},xAxis: {type: 'category',boundaryGap: false,data: _data.line.categories},yAxis: {type: 'value'},series: _data.line.series}const pieOption = {title: {text: _data.pie.title},tooltip: {trigger: 'item',formatter: '{a} <br/>{b} : {c} ({d}%)'},legend: {show: false},series: [{name: '访问来源',type: 'pie',radius: '55%',center: ['50%', '60%'],data: _data.pie.series,itemStyle: {emphasis: {shadowBlur: 10,shadowOffsetX: 0,shadowColor: 'rgba(0, 0, 0, 0.5)'}}}]}const lineChartDom = lineChart.valueconst pieChartDom = pieChart.valueconst lineChartInstance = echarts.init(lineChartDom)const pieChartInstance = echarts.init(pieChartDom)lineChartInstance.setOption(lineOption)pieChartInstance.setOption(pieOption)chartInstance1.value = lineChartInstancechartInstance2.value = pieChartInstance
}onMounted(() => {initChart()
})
</script><style lang="scss" scoped>
.container {display: flex;width: 1200px;justify-content: space-between;margin-top: 20px;position: relative;.chart {width: 49%;height: 400px;}.legend1 {position: absolute;bottom: -50px;left: 50px;}.legend2 {position: absolute;bottom: -50px;right: 50px;}
}
</style>
效果