3 小時(shí),手把手帶你完成【大屏可視化】系統(tǒng)(Vue3 + ECharts5)
Hello,大家好,我是 Sunday。
之前我在 B 站發(fā)布了一個(gè)大屏可視化的視頻【2023最新:ECharts 數(shù)據(jù)可視化大屏項(xiàng)目】 訪問地址:https://www.bilibili.com/video/BV1yu411E7cm/
圖片
該視頻到目前為止已經(jīng)有了 10多萬的播放,收藏也超過了 5000。
在很多同學(xué)學(xué)習(xí)該視頻的過程中,有很多同學(xué)問我:“Sunday老師,我在公司沒法看視頻,有沒有文檔也可以實(shí)現(xiàn)這個(gè)項(xiàng)目呢?”
答案:肯定是有的。
所以說,咱們今天這篇文章,就讓大家可以 不看視頻 只通過文檔,依然可以完成整個(gè)大屏可視化的項(xiàng)目!
1.前言
Hello,大家好,我是 Sunday。
這一小節(jié),我們將要來實(shí)現(xiàn)一個(gè)地圖可視化的項(xiàng)目,項(xiàng)目最終的實(shí)現(xiàn)效果如下:
圖片
整個(gè)可視化項(xiàng)目,一共分為 8 部分。
這八部分我們會(huì)按照,難易的順序來進(jìn)行繪制,盡量幫助大家可以以 逐步深入 的方式掌握大屏可視化的內(nèi)容。
- 橫向柱形圖
- 豎向柱形圖
- 雷達(dá)圖
- 環(huán)形圖
- 關(guān)系圖
- 數(shù)據(jù)云圖
- 數(shù)據(jù)展示圖
- 地圖可視化
那么明確好了,本章的內(nèi)容之后,下面這一章的項(xiàng)目開發(fā)吧~~
2.基于 vite 與 tailwindcss 創(chuàng)建大屏可視化項(xiàng)目
那么這一小節(jié),咱們就基于 vite+tailwindcss 構(gòu)建大屏可視化項(xiàng)目 imooc-visualization 。
- 基于 vite 創(chuàng)建項(xiàng)目,使用:
npm create vite@latest
- 選擇腳手架配置,使用 vue3:
? Project name: … imooc-visualization
? Select a framework: ? Vue
? Select a variant: ? JavaScript
接下來 導(dǎo)入 tailwindcss ,按照文檔給出的步驟即可。
因?yàn)樵蹅兊拇笃量梢暬?xiàng)目僅包含一個(gè)頁面,所以可以直接在 APP.vue 中進(jìn)行布局。
- 導(dǎo)入課程資源 imooc-visualization/src/assets,包含 fonts、imgs 和 MapData
- 在 imooc-visualization/src/App.vue 中進(jìn)行布局,整體的布局應(yīng)該分為 左、中、右 三部分,并創(chuàng)建對應(yīng)組件:
<template>
<div
class="bg-[url('assets/imgs/bg.jpg')] bg-cover bg-center h-screen text-white p-5 flex overflow-hidden"
>
<div class="flex-1 mr-5 bg-opacity-50 bg-slate-800 p-3 flex flex-col">
<!-- 橫向柱狀圖 -->
<HorizontalBar class="h-1/3 box-border pb-4" />
<!-- 雷達(dá)圖 -->
<RadarBar class="h-1/3 box-border pb-4" />
<!-- 數(shù)據(jù)傳遞關(guān)系圖 -->
<Relation class="h-1/3" />
</div>
<div class="w-1/2 mr-5 flex flex-col">
<!-- 數(shù)據(jù)展示圖 -->
<TotalData class="bg-opacity-50 bg-slate-800 p-3" />
<!-- 地圖可視化 -->
<MapChart class="bg-opacity-50 bg-slate-800 p-3 mt-4 flex-1" />
</div>
<div class="flex-1 bg-opacity-50 bg-slate-800 p-3 flex flex-col">
<!-- 豎向柱狀圖 -->
<VerticalBar class="h-1/3 box-border pb-4" />
<!-- 環(huán)形資源站比圖 -->
<RadiueBar class="h-1/3 box-border pb-4" />
<!-- 文檔云圖 -->
<WordCloud class="h-1/3 box-border" />
</div>
</div>
</template>
<script setup>
import HorizontalBar from './components/HorizontalBar.vue'
import RadarBar from './components/RadarBar.vue'
import Relation from './components/Relation.vue'
import TotalData from './components/TotalData.vue'
import MapChart from './components/MapChart.vue'
import VerticalBar from './components/VerticalBar.vue'
import RadiueBar from './components/RadiueBar.vue'
import WordCloud from './components/WordCloud.vue'
</script>
此時(shí),展示的內(nèi)容如下:
圖片
3.導(dǎo)入 echarts 與 axios ,獲取大屏動(dòng)態(tài)數(shù)據(jù)
在大屏可視化中,數(shù)據(jù)通常是動(dòng)態(tài)進(jìn)行展示的,所以我們需要依賴 axios 獲取服務(wù)端數(shù)據(jù),依賴 echarts 進(jìn)行展示,同時(shí)需要 定時(shí)獲取數(shù)據(jù),以保證數(shù)據(jù)的實(shí)時(shí)性。
- 安裝 echarts 與 axios:
npm i --save echarts@5.4.2 axios@1.4.0
- 創(chuàng)建 imooc-visualization/src/utils/request.js 文件:
import axios from 'axios'
const service = axios.create({
baseURL: 'https://api.imooc-web.lgdsunday.club/api',
timeout: 5000
})
// 請求攔截器
service.interceptors.request.use(
(config) => {
config.headers.icode = 'input you icode'
return config // 必須返回配置
},
(error) => {
return Promise.reject(error)
}
)
// 響應(yīng)攔截器
service.interceptors.response.use((response) => {
const { success, message, data } = response.data
// 要根據(jù)success的成功與否決定下面的操作
if (success) {
return data
} else {
return Promise.reject(new Error(message))
}
})
export default service
- 創(chuàng)建 imooc-visualization/src/api/visualization.js 文件:
import request from '@/utils/request.js'
/**
* 數(shù)據(jù)可視化
*/
export const getVisualization = () => {
return request({
url: '/visualization'
})
}
- 注意:**vite** 中默認(rèn)并不支持 **@** 符號,所以需要在 vite.config.js 中進(jìn)行配置:
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
// 為 ./src 提供別名 @
resolve: {
alias: {
'@': './src'
}
},
// 主動(dòng)開啟熱更新
server: {
hmr: true
}
})
- 在 imooc-visualization/src/App.vue 中發(fā)起數(shù)據(jù)請求,并通過定時(shí)器定時(shí)獲取數(shù)據(jù):
import { ref } from 'vue'
import { getVisualization } from '@/api/visualization.js'
const data = ref(null)
const loadData = async () => {
data.value = await getVisualization()
console.log(data.value)
}
loadData()
setInterval(() => {
loadData()
}, 3000)
那么此時(shí),我們就已經(jīng)拿到了大屏的動(dòng)態(tài)數(shù)據(jù)。下一小節(jié),我們就根據(jù)這個(gè)數(shù)據(jù),繪制橫向的柱形圖。
4.繪制大區(qū)橫向柱形圖
在上一小節(jié)中,我們拿到了大屏數(shù)據(jù),數(shù)據(jù)中的 regionData 即為 大區(qū)橫向柱形圖 數(shù)據(jù)。
所以,我們就可以依賴該數(shù)據(jù),進(jìn)行圖形繪制:
- 在 App.vue 中,數(shù)據(jù)獲取成功之后,在進(jìn)行渲染:
<div
class="bg-[url('assets/imgs/bg.jpg')] bg-cover bg-center h-screen text-white p-2 flex overflow-hidden"
v-if="data"
>
- 在 App.vue 中,傳遞指定數(shù)據(jù)到 HorizontalBar:
<!-- 橫向柱狀圖 -->
<HorizontalBar class="h-1/3 box-border pb-4" :data="data.regionData" />
- 在 imooc-visualization/src/components/HorizontalBar.vue 中,通過 defineProps 接收數(shù)據(jù):
const props = defineProps({
data: {
type: Object,
required: true
}
})
此時(shí),數(shù)據(jù)已經(jīng)得到。
接下來我們根據(jù)數(shù)據(jù)繪制 echarts 圖表。
echarts 圖表的繪制,大體分為三步:
- 根據(jù) DOM 實(shí)例,通過 echarts.init 方法,生成 echarts 實(shí)例
- 構(gòu)建 options 配置對象,整個(gè) echarts 的樣式,皆有該對象決定
- 最后通過 實(shí)例.setOption 方法,設(shè)置配置對象
配置對象是 echarts 中最復(fù)雜的部分,也就是 核心。
所有的配置項(xiàng)內(nèi)容,都可以通過 這里 進(jìn)行查閱
如果你之前 從未 使用過 echarts,那么特別建議你先看一下 快速入門 。
那么根據(jù)剛才所描述的三步,我們可以根據(jù)數(shù)據(jù),構(gòu)建出如下代碼:
<template>
<div>
<div>【大區(qū)數(shù)據(jù)信息】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
// 獲取 dom 實(shí)例
const target = ref(null)
// echarts 實(shí)例變量
let mChart = null
// 在 mounted 生命周期之后,實(shí)例化 echarts
onMounted(() => {
mChart = echarts.init(target.value)
// 渲染 echarts
renderChart()
})
// 渲染圖表
const renderChart = () => {
const options = {
// X 軸展示數(shù)據(jù)
xAxis: {
// 數(shù)據(jù)展示
type: 'value',
// 不顯示軸
show: false,
// 最大值(防止觸頂)
max: function (value) {
// 取整
return parseInt(value.max * 1.2)
}
},
// Y 軸展示選項(xiàng)
yAxis: {
type: 'category',
// 根據(jù)根據(jù)服務(wù)端數(shù)據(jù)篩選
data: props.data.regions.map((item) => item.name),
// 反向展示
inverse: true,
// 不展示軸線
axisLine: {
show: false
},
// 不展示刻度
axisTick: {
show: false // 取消 Y 軸刻度
},
// 文字色值
axisLabel: {
color: '#9EB1C8'
}
},
// echarts 網(wǎng)格繪制的位置,對應(yīng) 上、右、下、左
grid: {
top: 0,
right: 0,
bottom: 0,
left: 0,
// 計(jì)算邊距時(shí),包含標(biāo)簽
containLabel: true
},
// 柱形圖核心配置
series: [
{
// 圖表類型
type: 'bar',
// 數(shù)據(jù)篩選
data: props.data.regions.map((item) => ({
name: item.name,
value: item.value
})),
// 顯示背景
showBackground: true,
// 背景色
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
},
// 每個(gè)軸的樣式
itemStyle: {
color: '#479AD3', // 設(shè)置柱子的顏色
barBorderRadius: 5, // 設(shè)置柱子的圓角
shadowColor: 'rgba(0, 0, 0, 0.3)', // 設(shè)置柱子的陰影顏色
shadowBlur: 5 // 設(shè)置柱子的陰影模糊大小
},
// 軸寬度
barWidth: 12,
// 軸上的字體
label: {
show: true,
// 設(shè)置標(biāo)簽位置為右側(cè)
position: 'right',
textStyle: {
// 設(shè)置標(biāo)簽文本顏色
color: '#fff'
}
}
}
]
}
mChart.setOption(options)
}
那么此時(shí),一個(gè)基礎(chǔ)的橫向圖表繪制完成。
同時(shí),因?yàn)槲覀冃枰?動(dòng)態(tài)更新圖表,所以我們需要通過 watch 監(jiān)聽數(shù)據(jù)變化的行為,重新渲染視圖:
// 監(jiān)聽數(shù)據(jù)的變化,重新渲染圖表
watch(
() => props.data,
() => {
renderChart()
}
)
至此,橫向柱形圖渲染完成。
5.繪制服務(wù)豎向柱形圖
上一小節(jié),我們簡單的利用 echarts 的特性,繪制了橫向的柱形圖,那么接下來我們就趁熱打鐵,再來繪制一個(gè) 豎向柱形圖。
還是使用 visualization 接口,數(shù)據(jù)屬性為 serverData。
整體的流程還是分為三步:
- 把數(shù)據(jù)傳遞到 VerticalBar 組件
- 構(gòu)建 echarts 容器
- 利用 option 繪制圖表
最終代碼如下:
<template>
<div>
<div>【服務(wù)資源占用比】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
// 不要忘記從父組件傳遞數(shù)據(jù)
const props = defineProps({
data: {
type: Object,
required: true
}
})
const target = ref(null)
let mChart = null
onMounted(() => {
mChart = echarts.init(target.value)
renderChart()
})
const renderChart = () => {
const options = {
// X 軸展示選項(xiàng)
xAxis: {
type: 'category',
// 根據(jù)根據(jù)服務(wù)端數(shù)據(jù)篩選
data: props.data.servers.map((item) => item.name),
// 文字色值
axisLabel: {
color: '#9EB1C8'
}
},
// Y 軸展示數(shù)據(jù)
yAxis: {
// 數(shù)據(jù)展示
type: 'value',
// 不顯示軸
show: false,
// 最大值(防止觸頂)
max: function (value) {
// 取整
return parseInt(value.max * 1.2)
}
},
// echarts 網(wǎng)格繪制的位置,對應(yīng) 上、右、下、左
grid: {
top: 16,
right: 0,
bottom: 26,
left: -26,
// 計(jì)算邊距時(shí),包含標(biāo)簽
containLabel: true
},
// 柱形圖核心配置
series: {
// 柱形圖
type: 'bar',
// 數(shù)據(jù)篩選
data: props.data.servers.map((item) => ({
name: item.name,
value: item.value
})),
// 每個(gè)軸的樣式
itemStyle: {
color: '#479AD3', // 設(shè)置柱子的顏色
barBorderRadius: 5, // 設(shè)置柱子的圓角
shadowColor: 'rgba(0, 0, 0, 0.3)', // 設(shè)置柱子的陰影顏色
shadowBlur: 5 // 設(shè)置柱子的陰影模糊大小
},
// 柱子寬度
barWidth: 12,
// 文本
label: {
show: true,
// 設(shè)置標(biāo)簽位置為右側(cè)
position: 'top',
textStyle: {
// 設(shè)置標(biāo)簽文本顏色
color: '#fff'
},
formatter: '{c}%'
}
}
}
mChart.setOption(options)
}
// 監(jiān)聽數(shù)據(jù)的變化,重新渲染圖表
watch(
() => props.data,
() => {
renderChart()
}
)
</script>
6.繪制報(bào)警風(fēng)險(xiǎn)雷達(dá)圖
前面兩個(gè)小節(jié),咱們創(chuàng)建了基本的柱形圖。
通過柱形圖,咱們大致應(yīng)該了解了 echarts 基本的運(yùn)行方式。
所以說,接下來,咱們就來構(gòu)建一個(gè)稍微復(fù)雜一些的圖表 雷達(dá)圖。
雷達(dá)圖,使用 riskData 字段數(shù)據(jù)。
整個(gè)雷達(dá)圖的繪制,包含 5 個(gè)重要的屬性:
- radar:雷達(dá)圖坐標(biāo)系配置
- polar:坐標(biāo)居中
- angleAxis:坐標(biāo)角度
- radiusAxis:徑向軸
- series:圖表核心配置
最終渲染的代碼如下:
<template>
<div>
<div>【云端報(bào)警風(fēng)險(xiǎn)】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
// 不要忘記從父組件傳遞數(shù)據(jù)
const props = defineProps({
data: {
type: Object,
required: true
}
})
const target = ref(null)
let mChart = null
onMounted(() => {
mChart = echarts.init(target.value)
renderChart()
})
const renderChart = () => {
const options = {
// 雷達(dá)圖坐標(biāo)系配置
radar: {
// 坐標(biāo)系名
name: {
textStyle: {
color: '#05D5FF',
fontSize: 14
}
},
// 雷達(dá)繪制類型。polygon 多邊形
shape: 'polygon',
// 居中
center: ['50%', '50%'],
// 邊境
radius: '80%',
// 開始的角度(可以避免繪制到邊框之外)
startAngle: 120,
// 軸線配置
axisLine: {
lineStyle: {
color: 'rgba(5, 213, 255, .8)'
}
},
// 網(wǎng)格線配置
splitLine: {
show: true,
lineStyle: {
width: 1,
color: 'rgba(5, 213, 255, .8)' // 設(shè)置網(wǎng)格的顏色
}
},
// 指示器文字
indicator: props.data.risks.map((item) => ({
name: item.name,
max: 100
})),
// 不展示拆分區(qū)域
splitArea: {
show: false
}
},
// 坐標(biāo)居中
polar: {
center: ['50%', '50%'], // 默認(rèn)全局居中
radius: '0%'
},
// 坐標(biāo)角度
angleAxis: {
// 坐標(biāo)軸刻度最小值
min: 0,
// 坐標(biāo)軸分割間隔
interval: 5,
// 刻度增長逆時(shí)針
clockwise: false
},
// 徑向軸
radiusAxis: {
// 最小值
min: 0,
// 間隔
interval: 20,
// 不顯示分割線
splitLine: {
show: true
}
},
// 圖表核心配置
series: [
{
// 雷達(dá)圖
type: 'radar',
// 拐點(diǎn)的樣式,還可以取值'rect','angle'等
symbol: 'circle',
// 拐點(diǎn)的大小
symbolSize: 10,
// 折線拐點(diǎn)標(biāo)志的樣式
itemStyle: {
normal: {
color: '#05D5FF'
}
},
// 區(qū)域填充樣式
areaStyle: {
normal: {
color: '#05D5FF',
opacity: 0.5
}
},
// 線條樣式
lineStyle: {
width: 2,
color: '#05D5FF'
},
// 圖形上的文本標(biāo)簽
label: {
normal: {
show: true,
formatter: (params) => {
return params.value
},
color: '#fff'
}
},
// 數(shù)據(jù)
data: [
{
value: props.data.risks.map((item) => item.value)
}
]
}
]
}
mChart.setOption(options)
}
// 監(jiān)聽數(shù)據(jù)的變化,重新渲染圖表
watch(
() => props.data,
() => {
renderChart()
}
)
</script>
7.繪制異常處理雙環(huán)形圖
在前面的 echarts 圖表繪制中,整體的繪制還是比較簡單的。那么接下來咱們就來繪制一個(gè)稍微復(fù)雜一些的圖形 雙環(huán)形圖。
圖片
首選我們現(xiàn)在分析一下雙環(huán)形圖的構(gòu)圖原理:
- 在 echarts 中,是沒有提供環(huán)形圖圖表的。所以這里的環(huán)形圖本質(zhì)上是通過 餅圖 來進(jìn)行繪制的(只需要把餅圖的內(nèi)邊距調(diào)大,外邊距調(diào)小,就可以實(shí)現(xiàn)環(huán)形)。
- 每個(gè)環(huán)都是都是有兩個(gè)餅圖組成。其中一個(gè)餅圖表示上層展示,一個(gè)餅圖作為底層的背景。只需要讓它們位置一致就可以得到對應(yīng)的圖形
- 動(dòng)態(tài)變化每一對環(huán)形的位置,從而得到一個(gè)逐層向內(nèi)的環(huán)形展示
那么,咱們分析完成雙環(huán)形的原理之后,接下來咱們就根據(jù)數(shù)據(jù) abnormalData 來實(shí)現(xiàn)這個(gè)雙環(huán)形:
<template>
<div>
<div>【大區(qū)異常處理】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
data: {
type: Object,
required: true
}
})
const target = ref(null)
let mChart = null
onMounted(() => {
mChart = echarts.init(target.value)
renderChart()
})
/**
* 雙環(huán)形圖繪制原理:
* 1. 環(huán)形圖通過餅圖繪制。內(nèi)外邊距的距離減小,即為環(huán)形。環(huán)形中心點(diǎn)需要不斷改變,否則會(huì)重疊
* 2. 環(huán)形圖繪制分為 上層和底層 兩部分。上層作為繪制進(jìn)度,底層作為背景圖
* 3. 依據(jù) getSeriesData 生成對應(yīng)的 上層和底層 series 數(shù)據(jù),進(jìn)行渲染
*/
const getSeriesData = () => {
const series = []
props.data.abnormals.forEach((item, index) => {
// 上層環(huán)形繪制
series.push({
name: item.name,
// 使用餅圖繪制,減少餅圖寬度即為環(huán)形圖
type: 'pie',
// 逆時(shí)針排布
clockWise: false,
// 不展示鼠標(biāo)移入動(dòng)畫
hoverAnimation: false,
// 半徑位置,需要依次遞減,否則會(huì)重復(fù)在一處進(jìn)行展示
radius: [73 - index * 15 + '%', 68 - index * 15 + '%'],
// 中心點(diǎn)
center: ['55%', '55%'],
// 不展示 label
label: { show: false },
// 數(shù)據(jù)配置
data: [
// 設(shè)置數(shù)據(jù)與名稱
{ value: item.value, name: item.name },
// 最大數(shù)據(jù),展示比例
{
value: 1000,
name: '',
itemStyle: { color: 'rgba(0,0,0,0)', borderWidth: 0 },
tooltip: { show: false },
hoverAnimation: false
}
]
})
// 底層圖
series.push({
name: item.name,
type: 'pie',
// 圖形不響應(yīng)事件
silent: true,
// z-index: 置于底層
z: 1,
// 逆時(shí)針排布
clockWise: false,
// 不展示鼠標(biāo)移入動(dòng)畫
hoverAnimation: false,
// 半徑位置,需要依次遞減,否則會(huì)重復(fù)在一處進(jìn)行展示
radius: [73 - index * 15 + '%', 68 - index * 15 + '%'],
// 中心點(diǎn)
center: ['55%', '55%'],
// 不展示 label
label: { show: false },
// 數(shù)據(jù)
data: [
// 繪制底線 75%
{
value: 7.5,
itemStyle: { color: 'rgb(3, 31, 62)', borderWidth: 0 },
tooltip: { show: false },
hoverAnimation: false
},
// 繪制底線 25% 透明區(qū)域
{
value: 2.5,
name: '',
itemStyle: { color: 'rgba(0,0,0,0)', borderWidth: 0 },
tooltip: { show: false },
hoverAnimation: false
}
]
})
})
return series
}
const renderChart = () => {
const options = {
// 圖例配置
legend: {
show: true,
// 圖例色塊
icon: 'circle',
// 位置
top: '14%',
left: '60%',
// 展示數(shù)據(jù)
data: props.data.abnormals.map((item) => item.name),
// 總寬度(一列)
width: -5,
// 每個(gè)色塊的寬
itemWidth: 10,
// 每個(gè)色塊的高度
itemHeight: 10,
// item 間距
itemGap: 6,
// 展示內(nèi)容
formatter: function (name) {
return '{title|' + name + '}'
},
// 字體配置
textStyle: {
rich: {
title: {
fontSize: 12,
lineHeight: 5,
color: 'rgba(255,255,255,0.8)'
}
}
}
},
// 提示層
tooltip: {
show: true,
trigger: 'item',
formatter: '{a}<br>:{c}(k6zqhab033oa%)'
},
// Y 軸展示選項(xiàng)
yAxis: [
{
type: 'category',
// 反向展示
inverse: true,
// 不展示軸線
axisLine: {
show: false
},
// 不展示刻度
axisTick: {
show: false
}
}
],
// X 軸不展示
xAxis: [
{
show: false
}
],
// 每兩個(gè)標(biāo)記一條線
series: getSeriesData()
}
mChart.setOption(options)
}
// 監(jiān)聽數(shù)據(jù)的變化,重新渲染圖表
watch(
() => props.data,
() => {
renderChart()
}
)
</script>
8.繪制數(shù)據(jù)傳遞關(guān)系圖
在前面,我們繪制了很多基本的 echarts 圖表。
但是在實(shí)際的企業(yè)開發(fā)中,可能會(huì)存在一些比較特殊的圖表類型,比如 信息傳遞圖 :
想要完成這樣的圖表,咱們需要借助兩個(gè) series 中的圖表類型:
- graph:它表示 關(guān)系圖,主要用于展現(xiàn)節(jié)點(diǎn)以及節(jié)點(diǎn)之間的關(guān)系數(shù)據(jù)。
- lines:它表示 路徑圖,主要用來繪制兩個(gè)節(jié)點(diǎn) 從起點(diǎn)到終點(diǎn) 的路徑。
兩者之間進(jìn)行結(jié)合,就可以得到咱們上面的數(shù)據(jù)關(guān)系圖了。
具體代碼如下:
<template>
<div>
<div>【數(shù)據(jù)傳遞信息】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
data: {
type: Object,
required: true
}
})
// 獲取 dom 實(shí)例
const target = ref(null)
// echarts 實(shí)例變量
let mChart = null
// 在 mounted 生命周期之后,實(shí)例化 echarts
onMounted(() => {
mChart = echarts.init(target.value)
// 渲染 echarts
renderChart()
})
// 渲染圖表
const renderChart = () => {
const options = {
// X 軸不需要展示
xAxis: {
show: false,
type: 'value'
},
// X 軸不需要展示
yAxis: {
show: false,
type: 'value'
},
// 核心數(shù)據(jù)配置
series: [
{
// 用于展現(xiàn)節(jié)點(diǎn)以及節(jié)點(diǎn)之間的關(guān)系數(shù)據(jù)
type: 'graph',
// 不采用任何布局
layout: 'none',
// 使用二維的直角坐標(biāo)系
coordinateSystem: 'cartesian2d',
// 節(jié)點(diǎn)標(biāo)記的大小
symbolSize: 26,
// z-index
z: 3,
// 邊界標(biāo)簽(線條文字)
edgeLabel: {
normal: {
show: true,
color: '#fff',
textStyle: {
fontSize: 14
},
formatter: function (params) {
let txt = ''
if (params.data.speed !== undefined) {
txt = params.data.speed
}
return txt
}
}
},
// 圓餅下文字
label: {
normal: {
show: true,
position: 'bottom',
color: '#5e5e5e'
}
},
// 邊兩端的標(biāo)記類型
edgeSymbol: ['none', 'arrow'],
// 邊兩端的標(biāo)記大小
edgeSymbolSize: 8,
// 圓數(shù)據(jù)
data: props.data.relations.map((item) => {
// id 為 0 ,表示數(shù)據(jù)中心,數(shù)據(jù)中心單獨(dú)設(shè)置
if (item.id !== 0) {
return {
name: item.name,
category: 0,
active: true,
// 位置
value: item.value
}
} else {
return {
name: item.name,
// 位置
value: item.value,
// 數(shù)據(jù)中心圓的大小
symbolSize: 100,
// 圓的樣式
itemStyle: {
normal: {
// 漸變色
color: {
colorStops: [
{ offset: 0, color: '#157eff' },
{ offset: 1, color: '#35c2ff' }
]
}
}
},
// 字體
label: { normal: { fontSize: '14' } }
}
}
}),
// 線
links: props.data.relations.map((item, index) => ({
// 方向
source: item.source,
target: item.target,
// 線上的文字
speed: `${item.speed}kb/s`,
// 線的樣式
lineStyle: { normal: { color: '#12b5d0', curveness: 0.2 } },
// 文字位置
label: {
show: true,
position: 'middle',
offset: [10, 0]
}
}))
},
{
// 用于帶有起點(diǎn)和終點(diǎn)信息的線數(shù)據(jù)的繪制
type: 'lines',
// 使用二維的直角坐標(biāo)系
coordinateSystem: 'cartesian2d',
// z-index
z: 1,
// 線特效的配置
effect: {
show: true,
smooth: false,
trailLength: 0,
symbol: 'arrow',
color: 'rgba(55,155,255,0.5)',
symbolSize: 12
},
// 線的樣式
lineStyle: {
normal: {
curveness: 0.2
}
},
// 線的數(shù)據(jù)級,前后線需要重合。數(shù)據(jù)固定
data: [
[{ coord: [0, 300] }, { coord: [50, 200] }],
[{ coord: [0, 100] }, { coord: [50, 200] }],
[{ coord: [50, 200] }, { coord: [100, 100] }],
[{ coord: [50, 200] }, { coord: [100, 300] }]
]
}
]
}
mChart.setOption(options)
}
// 監(jiān)聽數(shù)據(jù)的變化,重新渲染圖表
watch(
() => props.data,
() => {
renderChart()
}
)
</script>
9.繪制關(guān)鍵詞條文檔云圖
在前面的圖表中,我們都是基于 echarts 的原生圖表支持進(jìn)行的繪制。但是有些情況下,某些圖表我們需要依賴插件才可以完成對應(yīng)的繪制,比如:文檔云圖
想要繪制這樣的文檔云圖,那么我們首先必須先安裝 echarts-wordcloud,執(zhí)行:
cnpm i --save echarts-wordcloud@2.1.0
安裝完成之后,需要在使用 wordcloud 的組件上,導(dǎo)入 import 'echarts-wordcloud'。
那么這樣,我們就可以基于 wordCloudData 數(shù)據(jù)渲染文檔云圖了:
<template>
<div>
<div>【關(guān)鍵詞條】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
import 'echarts-wordcloud'
const props = defineProps({
data: {
type: Object,
required: true
}
})
// 獲取 dom 實(shí)例
const target = ref(null)
// echarts 實(shí)例變量
let mChart = null
// 在 mounted 生命周期之后,實(shí)例化 echarts
onMounted(() => {
mChart = echarts.init(target.value)
// 渲染 echarts
renderChart()
})
/**
* 生成隨機(jī)色值
*/
const randomRGB = () => {
const r = Math.floor(Math.random() * 255)
const g = Math.floor(Math.random() * 255)
const b = Math.floor(Math.random() * 255)
return `rgb(${r}, ${g}, $)`
}
// 渲染圖表
const renderChart = () => {
const options = {
series: [
{
// 類型
type: 'wordCloud',
// 數(shù)據(jù)映射文本的大小范圍
sizeRange: [8, 46],
// 文字旋轉(zhuǎn)范圍
rotationRange: [0, 0],
// 單詞之間的間距
gridSize: 0,
// 渲染動(dòng)畫
layoutAnimation: true,
// 字體
textStyle: {
// 隨機(jī)色值
color: randomRGB
},
// 高亮字體
emphasis: {
textStyle: {
fontWeight: 'bold',
color: '#000'
}
},
// 數(shù)據(jù)
data: props.data.datas
}
]
}
mChart.setOption(options)
}
// 監(jiān)聽數(shù)據(jù)的變化,重新渲染圖表
watch(
() => props.data,
() => {
renderChart()
}
)
</script>
10.繪制數(shù)據(jù)總覽圖
通常在大屏可視化項(xiàng)目中,除了包含基本的圖表之外,還會(huì)存在一些數(shù)據(jù)概覽的內(nèi)容。這就是我們這一小節(jié)主要講解的東西。
咱們這里所實(shí)現(xiàn)的數(shù)據(jù)概覽,主要包含兩塊功能:
- 數(shù)值自增
- 數(shù)值漸變 + 特殊字體
首先針對于數(shù)值自增而言,主要可以通過 countup.js 進(jìn)行實(shí)現(xiàn):
- 安裝 countup.js:
cnpm i --save countup.js@2.6.2
- 實(shí)現(xiàn)如下布局代碼:
<template>
<div class="p-6">
<div class="text-slate-300 text-center">
數(shù)據(jù)總量:
<span
ref="totalCountTarget"
class="text-7xl ml-2 mr-2 font-bold"
>
679,473,929
</span>
條記錄
</div>
<div class="mt-3 flex flex-wrap">
<div class="w-1/3 text-center text-slate-400 text-sm">
華北:
<span ref="city1" class="text-[#5DC5EF] text-3xl"> 8,778,988 </span>
</div>
<div class="w-1/3 text-center text-slate-400 text-sm">
東北:<span ref="city2" class="text-[#5DC5EF] text-3xl">8,778,988</span>
</div>
<div class="w-1/3 text-center text-slate-400 text-sm">
華東:<span ref="city3" class="text-[#5DC5EF] text-3xl">8,778,988</span>
</div>
<div class="w-1/3 text-center text-slate-400 text-sm">
中南:<span ref="city4" class="text-[#5DC5EF] text-3xl">8,778,988</span>
</div>
<div class="w-1/3 text-center text-slate-400 text-sm">
西南:<span ref="city5" class="text-[#5DC5EF] text-3xl">8,778,988</span>
</div>
<div class="w-1/3 text-center text-slate-400 text-sm">
西北:<span ref="city6" class="text-[#5DC5EF] text-3xl">8,778,988</span>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { CountUp } from 'countup.js'
const props = defineProps({
data: {
type: Object,
required: true
}
})
const totalCountTarget = ref(null)
const city1 = ref(null)
const city2 = ref(null)
const city3 = ref(null)
const city4 = ref(null)
const city5 = ref(null)
const city6 = ref(null)
onMounted(() => {
new CountUp(totalCountTarget.value, props.data.total).start()
new CountUp(city1.value, props.data.hb).start()
new CountUp(city2.value, props.data.db).start()
new CountUp(city3.value, props.data.hd).start()
new CountUp(city4.value, props.data.zn).start()
new CountUp(city5.value, props.data.xn).start()
new CountUp(city6.value, props.data.xb).start()
})
</script>
那么此時(shí),我們就實(shí)現(xiàn)了數(shù)據(jù)自增的功能。
接下來第二塊是實(shí)現(xiàn)數(shù)值漸變 + 特殊字體。
數(shù)值漸變可以直接通過 css 進(jìn)行實(shí)現(xiàn),本質(zhì)上是通過 背景漸變 + 字體鏤空 進(jìn)行實(shí)現(xiàn)。
而特殊字體需要導(dǎo)入資料中 07:圖表與大屏可視化 -> assets -> fonts 中的字體
- 導(dǎo)入電子表字體到 src/assets/fonts/FX-LED.ttf 中
- 在 src/style.css 中寫入如下 css:
@font-face {
font-family: 'Electronic';
src: url('./assets/fonts/FX-LED.ttf') format('truetype');
}
.text-gradient {
/* 背景漸變 */
background-image: linear-gradient(to bottom, #e5e4ea, #5EA8F2);
/* 元素背景延伸到文本 */
-webkit-background-clip: text;
/* 文本字符填充顏色透明 */
-webkit-text-fill-color: transparent;
}
- 在 src/components/TotalData.vue 中,為每個(gè) span 添加 font-[Electronic] 類名
- 單獨(dú)為數(shù)據(jù)總量添加 text-gradient 類名
11.地圖可視化分析與時(shí)間軸圖表繪制
那么到目前,我們就只剩下最后一塊,也是最復(fù)雜的一塊功能需要進(jìn)行實(shí)現(xiàn)了。
這一塊圖表,可以被分為四個(gè)部分:
- 時(shí)間軸
- 橫向柱形圖
- 地圖
- 繪制地圖散點(diǎn)圖
所以針對這樣的功能,咱們將分成四個(gè)小節(jié)分步進(jìn)行繪制。
那么這一小節(jié),咱們就先就來實(shí)現(xiàn) 時(shí)間軸 繪制。
時(shí)間軸繪制,需要使用到 echarts 中的 timeline 屬性,具體代碼如下:
<template>
<div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
data: {
type: Object,
required: true
}
})
const target = ref(null)
let mChart = null
onMounted(() => {
mChart = echarts.init(target.value)
renderChart()
})
const renderChart = () => {
let options = {
// 時(shí)間線,提供了在多個(gè) ECharts option 間進(jìn)行切換
timeline: {
// 數(shù)據(jù)
data: props.data.voltageLevel,
// 類目軸
axisType: 'category',
// 自動(dòng)切換
autoPlay: true,
// 間隔時(shí)間
playInterval: 3000,
// 位置
left: '10%',
right: '10%',
bottom: '0%',
width: '80%',
// 軸的文本標(biāo)簽
label: {
// 默認(rèn)狀態(tài)
normal: {
textStyle: {
color: '#ddd'
}
},
// 高亮狀態(tài)
emphasis: {
textStyle: {
color: '#fff'
}
}
},
// 文字大小
symbolSize: 10,
// 線的樣式
lineStyle: {
color: '#555'
},
// 選中點(diǎn)的樣式
checkpointStyle: {
borderColor: '#888',
borderWidth: 2
},
// 控件樣式
controlStyle: {
// 上一步按鈕
showNextBtn: true,
// 下一步按鈕
showPrevBtn: true,
// 默認(rèn)樣式
normal: {
color: '#666',
borderColor: '#666'
},
// 高亮樣式
emphasis: {
color: '#aaa',
borderColor: '#aaa'
}
}
}
}
mChart.setOption(options)
}
</script>
12.地圖可視化繪制動(dòng)態(tài)柱形圖
這一小節(jié),咱們來繪制地圖可視化中的動(dòng)態(tài)柱形圖。
柱形圖的繪制,對咱們現(xiàn)在而言,應(yīng)該是有一些熟悉的了,同時(shí)因?yàn)橛袝r(shí)間軸的存在,所以我們可以直接添加 options 屬性,來為時(shí)間軸動(dòng)態(tài)綁定多個(gè)圖表。
對應(yīng)的繪制代碼如下:
const renderChart = () => {
let options = {
// 時(shí)間線,提供了在多個(gè) ECharts option 間進(jìn)行切換
timeline: {...},
// 柱形圖右側(cè)展示
baseOption: {
grid: {
right: '2%',
top: '15%',
bottom: '10%',
width: '20%'
}
},
// 綁定時(shí)間軸的多個(gè)圖表
options: []
}
// 為每一年度的圖表添加數(shù)據(jù)
props.data.voltageLevel.forEach((item, index) => {
options.options.push({
// 背景色
backgroundColor: '#142037',
title: [
// 主標(biāo)題,對應(yīng)地圖
{
text: '2019-2023 年度數(shù)據(jù)統(tǒng)計(jì)',
left: '0%',
top: '0',
textStyle: {
color: '#ccc',
fontSize: 30
}
},
// 副標(biāo)題,對應(yīng)柱形圖
{
id: 'statistic',
text: item + '年數(shù)據(jù)統(tǒng)計(jì)情況',
right: '0%',
top: '4%',
textStyle: {
color: '#ccc',
fontSize: 20
}
}
],
// X 軸配置
xAxis: {
// 數(shù)據(jù)軸
type: 'value',
// 脫離 0 值比例
scale: true,
// 位置
position: 'top',
// 不顯示分割線
splitLine: {
show: false
},
// 不顯示軸線
axisLine: {
show: false
},
// 不顯示刻度尺
axisTick: {
show: false
},
// 類別文字
axisLabel: {
margin: 2,
textStyle: {
color: '#aaa'
}
}
},
// Y 軸
yAxis: {
// 選項(xiàng)軸
type: 'category',
// 軸線
axisLine: {
show: true,
lineStyle: {
color: '#ddd'
}
},
// 軸刻度
axisTick: {
show: false,
lineStyle: {
color: '#ddd'
}
},
// 軸標(biāo)簽
axisLabel: {
interval: 0,
textStyle: {
color: '#ddd'
}
},
// 根據(jù)年份,獲取對應(yīng)數(shù)據(jù)
data: props.data.categoryData[item].map((item) => item.name)
},
// 核心配置
series: [
{
zlevel: 1.5,
// 柱形圖
type: 'bar',
// 每個(gè)柱子的色值
itemStyle: {
normal: {
color: props.data.colors[index]
}
},
// 根據(jù)年份,獲取對應(yīng)數(shù)據(jù)
data: props.data.categoryData[item].map((item) => item.value)
}
]
})
})
mChart.setOption(options)
}
13.地圖可視化,繪制地圖
最后,我們來繪制地圖。
想要繪制地圖,那么需要首先 導(dǎo)入地圖的 **json** 文件:
import mapJson from '@/assets/MapData/china.json'
echarts.registerMap('china', mapJson)
然后在 baseOption 中,添加 geo 作為地圖配置:
baseOption: {
...
// 中國地圖
geo: {
// 展示
show: true,
// 中國地圖
map: 'china',
// 開啟縮放
roam: true,
// 初始縮放
zoom: 0.8,
// 中心點(diǎn)
center: [113.83531246, 34.0267395887],
// 默認(rèn)狀態(tài)的省份樣式
itemStyle: {
normal: {
// 邊框色值
borderColor: 'rgba(147, 235, 248, 1)',
// 邊框?qū)挾? borderWidth: 1,
// 區(qū)域顏色
areaColor: {
// 經(jīng)向色值
type: 'radial',
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [
// 0% 處的顏色
{
offset: 0,
color: 'rgba(147, 235, 248, 0)'
},
// 100% 處的顏色
{
offset: 1,
color: 'rgba(147, 235, 248, .2)'
}
],
// 缺省為 false
globalCoord: false
}
},
// 鼠標(biāo)移入的色值
emphasis: {
areaColor: '#389BB7',
borderWidth: 0
}
}
}
}
14.地圖可視化散點(diǎn)圖繪制
最后只需要在 series 中繪制散點(diǎn)圖即可:
// 散點(diǎn)圖
{
// 散點(diǎn)(氣泡)圖
type: 'effectScatter',
// 使用地理坐標(biāo)系
coordinateSystem: 'geo',
// 數(shù)據(jù)
data: props.data.topData[item],
// 標(biāo)記大小
symbolSize: function (val) {
return val[2] / 4
},
// 繪制完成后顯示特效
showEffectOn: 'render',
// 展示漣漪特效
rippleEffect: {
brushType: 'stroke'
},
// 文字
label: {
normal: {
formatter: '',
position: 'right',
show: true
}
},
// 每一項(xiàng)的配置
itemStyle: {
normal: {
color: props.data.colors[index],
// 陰影配置
shadowBlur: 5,
shadowColor: props.data.colors[index]
}
},
zlevel: 1
}
15.AntV 概述與 G2 開發(fā)柱形圖
那么到現(xiàn)在為止,咱們就已經(jīng)利用利用 echarts 完成了一個(gè)大屏可視化項(xiàng)目的開發(fā)。
但是大家需要知道的是,除了 ehcarts 之外,還有一些其他的框架,也可以實(shí)現(xiàn)可視化圖表開發(fā),比如 antv。
AntV:由螞蟻集團(tuán)數(shù)據(jù)可視化團(tuán)隊(duì)發(fā)布,內(nèi)部進(jìn)行了庫的細(xì)分:
- G2:類似于 Echarts ,提供了各種常見的圖表。課程中的 大屏可視化項(xiàng)目 ,也可以基于此實(shí)現(xiàn)
- S2:表格數(shù)據(jù)圖形化
- G6:數(shù)據(jù)關(guān)系可視化。比如腦圖實(shí)現(xiàn)
- X6:圖形編輯引擎。比如流程圖、ER 圖構(gòu)建
- L7:地理空間數(shù)據(jù)可視化。與地圖相關(guān)
- F2:移動(dòng)端數(shù)據(jù)可視化。可以理解為 G2 的移動(dòng)端版本
- AVA:可視化生成框架。可以理解為可視化的低代碼框架
Antv 從使用中和 echarts 會(huì)有一些不同,我們可以通過這個(gè) 案例 來查看 G2 的基本使用:
- 通過 vite 創(chuàng)建新的項(xiàng)目 antv-g2:
npm create vite@latest
- 在 App.vue 中寫入如下代碼:
<template>
<div>
<div id="container"></div>
</div>
</template>
<script setup>
import { Chart } from '@antv/g2'
import { onMounted } from 'vue'
// 準(zhǔn)備數(shù)據(jù)
const data = [
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 }
]
onMounted(() => {
// 初始化圖表實(shí)例
const chart = new Chart({
container: 'container',
theme: 'classic'
})
// 聲明可視化
chart
.interval() // 創(chuàng)建一個(gè) Interval 標(biāo)記
.data(data) // 綁定數(shù)據(jù)
.encode('x', 'genre') // 編碼 x 通道
.encode('y', 'sold') // 編碼 y 通道
// 渲染可視化
chart.render()
})
</script>
這樣,咱們可以利用 G2 構(gòu)建一個(gè)基本的柱形圖
16.大屏數(shù)據(jù)展示組件庫 DataV
在本章的最后,我為大家介紹最后一個(gè)東西 DataV。
DataV 是一個(gè) 大屏數(shù)據(jù)展示組件庫,同時(shí)提供了 vue 版本和 react 版本。
它的使用和其他的組件庫一樣,都是安裝之后全局導(dǎo)入組件和局部導(dǎo)入組件。只不過它的組件會(huì)更加偏向于 大屏可視化的場景。
它內(nèi)部提供的組件并不多,大家可以通過視頻查看對應(yīng)的演示。在文檔中,咱們就不過多贅述了。
17.總結(jié)
那么到這里,咱們本章的主要內(nèi)容就已經(jīng)全部完成了。
在本章中,我們首先利用 vite + tailwindcss 構(gòu)建了一個(gè)大屏可視化的項(xiàng)目。
然后通過 echarts 完成了大屏可視化的功能。在項(xiàng)目中,每一個(gè)圖表都是一個(gè)單獨(dú)的組件。
在本章的最后,我們有學(xué)習(xí)了一下 antv 并且利用 G2 庫 實(shí)現(xiàn)了簡單的柱形圖。