Vue使用Echarts實(shí)現(xiàn)橫向柱狀圖,并通過WebSocket即時(shí)通訊更新
先看下效果圖
并且數(shù)據(jù)每隔3秒自動(dòng)變換一次

先看下后臺(tái)返回的數(shù)據(jù)結(jié)構(gòu)是什么樣子的
[
{
"name":"商家1",
"value":"99"
},
{
"name":"商家2",
"value":"199"
},
{
"name":"商家3",
"value":"222"
},
{
"name":"商家4",
"value":"99"
},
{
"name":"商家5",
"value":"499"
},
{
"name":"商家6",
"value":"252"
},
{
"name":"商家7",
"value":"199"
},
{
"name":"商家8",
"value":"29"
},
{
"name":"商家9",
"value":"232"
},{
"name":"商家10",
"value":"99"
},
{
"name":"商家11",
"value":"77"
},
{
"name":"商家12",
"value":"82"
},
{
"name":"商家13",
"value":"99"
},
{
"name":"商家14",
"value":"19"
},
{
"name":"商家15",
"value":"22"
},
{
"name":"商家16",
"value":"522"
}
]
開始實(shí)現(xiàn)前端的代碼
html
<div class="com-page"> <div class="com-container"> <div class="com-chart" ref="seller_ref"></div> </div> </div>
css
html,body,#app{
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
.com-page {
width: 100%;
height: 100%;
overflow: hidden;
}
.com-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.com-chart {
width: 100%;
height: 100%;
overflow: hidden;
}
data
data() {
return {
chartInstance: null, //初始化echartInstance對(duì)象
allData: null, //接收的后臺(tái)數(shù)據(jù)
currentPage: 1, //當(dāng)前顯示的頁數(shù)
totalPage: 0, //一共有多少頁
timerId: null //定時(shí)器標(biāo)識(shí)
}
},
methods
initChart方法
initChart() {
//初始化echartInstance對(duì)象
//chalk是我們定義的主題,echarts官方有案例,怎么使用可以百度一下,不喜歡可以直接刪掉
this.chartInstance = this.$echarts.init(this.$refs.seller_ref, 'chalk')
//對(duì)圖表初始化配置對(duì)控制
const initOption = {
title: {
text: '▎商家銷售統(tǒng)計(jì)',
left: 20,
top: 20
},
grid: {
top: '20%',
left: '3%',
right: '6%',
bottom: '3%',
containLabel: true // 距離是包含坐標(biāo)軸上的文字
},
xAxis: {
type: 'value'
},
yAxis: {
type: 'category'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'line',
z: 0,
lineStyle: {
color: '#2D3443'
}
}
},
series: [{
type: 'bar',
label: {
show: true,
position: 'right',
textStyle: {
color: 'white'
}
},
itemStyle: {
// 指明顏色漸變的方向
// 指明不同百分比之下顏色的值
color: new this.$echarts.graphic.LinearGradient(0, 0, 1, 0, [
// 百分之0狀態(tài)之下的顏色值
{
offset: 0,
color: '#5052EE'
},
// 百分之100狀態(tài)之下的顏色值
{
offset: 1,
color: '#AB6EE5'
}
])
}
}]
}
this.chartInstance.setOption(initOption)
//對(duì)圖表對(duì)象進(jìn)行鼠標(biāo)事件監(jiān)聽
//鼠標(biāo)移入,定時(shí)器停止
this.chartInstance.on('mouseover', () => {
clearInterval(this.timerId)
})
//鼠標(biāo)移出,定時(shí)器開始
this.chartInstance.on('mouseout', () => {
this.startInterval()
})
},
getData方法
這里還是用http請(qǐng)求獲取的數(shù)據(jù),后面我再講怎么用WebSocket獲取我們的數(shù)據(jù)
async getData() {
const {
data: res
} = await this.$http.get('seller')
this.allData = res
//對(duì)數(shù)據(jù)進(jìn)行排序
this.allData.sort((a, b) => {
return a.value - b.value //從小到大排序
})
//每五個(gè)元素顯示一頁
this.totalPage = this.allData.length % 5 === 0 ? this.allData.length / 5 : this.allData.length / 5 + 1
this.updateChart()
//啟動(dòng)定時(shí)器
this.startInterval()
},
updateChart方法
//更新圖表
updateChart() {
//起始的位置
const start = (this.currentPage - 1) * 5
//結(jié)束的位置
//起始為0,所以展示1-5
const end = this.currentPage * 5
const showData = this.allData.slice(start, end)
const sellerNames = showData.map((item) => {
return item.name
})
const sellerValue = showData.map((item) => {
return item.value
})
const dataOption = {
yAxis: {
data: sellerNames
},
series: [{
data: sellerValue
}]
}
this.chartInstance.setOption(dataOption)
},
startInterval方法
//定時(shí)器,數(shù)據(jù)每3秒更新一次
startInterval() {
if (this.timerId) {
clearInterval(this.timerId)
}
this.timerId = setInterval(() => {
this.currentPage++
if (this.currentPage > this.totalPage) {
this.currentPage = 1
}
this.updateChart()
}, 3000)
},
screenAdapter方法
//屏幕適配
screenAdapter() {
const titleFontSize = this.$refs.seller_ref.offsetWidth / 100 * 3.6
console.log(titleFontSize)
const adapterOption = {
title: {
textStyle: {
fontSize: titleFontSize
}
},
tooltip: {
axisPointer: {
lineStyle: {
width: titleFontSize
}
}
},
series: [{
barWidth: titleFontSize,
//圓角大小
itemStyle: {
barBorderRadius: [0, titleFontSize / 2, titleFontSize / 2, 0],
}
}]
}
this.chartInstance.setOption(adapterOption)
//手動(dòng)調(diào)用圖表對(duì)象resize
this.chartInstance.resize()
}
mounted
mounted() {
this.initChart()
this.getData()
window.addEventListener('resize', this.screenAdapter)
this.screenAdapter()
},
destroyed
destroyed() {
clearInterval(this.timerId)
window.removeEventListener('resize', this.screenAdapter)
},
好了,完事
下面我把如何用WebSocket獲取數(shù)據(jù)說一下
封裝了一個(gè)WebSocket
export default class SocketService {
/**
* 單例
*/
static instance = null
static get Instance() {
if (!this.instance) {
this.instance = new SocketService()
}
return this.instance
}
// 和服務(wù)端連接的socket對(duì)象
ws = null
// 存儲(chǔ)回調(diào)函數(shù)
callBackMapping = {}
// 標(biāo)識(shí)是否連接成功
connected = false
// 記錄重試的次數(shù)
sendRetryCount = 0
// 重新連接嘗試的次數(shù)
connectRetryCount = 0
// 定義連接服務(wù)器的方法
connect() {
// 連接服務(wù)器
if (!window.WebSocket) {
return console.log('您的瀏覽器不支持WebSocket')
}
this.ws = new WebSocket('ws://localhost:9998')
// 連接成功的事件
this.ws.onopen = () => {
console.log('連接服務(wù)端成功了')
this.connected = true
// 重置重新連接的次數(shù)
this.connectRetryCount = 0
}
// 1.連接服務(wù)端失敗
// 2.當(dāng)連接成功之后, 服務(wù)器關(guān)閉的情況
this.ws.onclose = () => {
console.log('連接服務(wù)端失敗')
this.connected = false
this.connectRetryCount++
setTimeout(() => {
this.connect()
}, 500 * this.connectRetryCount)
}
// 得到服務(wù)端發(fā)送過來的數(shù)據(jù)
this.ws.onmessage = msg => {
console.log('從服務(wù)端獲取到了數(shù)據(jù)')
// 真正服務(wù)端發(fā)送過來的原始數(shù)據(jù)時(shí)在msg中的data字段
// console.log(msg.data)
const recvData = JSON.parse(msg.data)
const socketType = recvData.socketType
// 判斷回調(diào)函數(shù)是否存在
if (this.callBackMapping[socketType]) {
const action = recvData.action
if (action === 'getData') {
const realData = JSON.parse(recvData.data)
this.callBackMapping[socketType].call(this, realData)
} else if (action === 'fullScreen') {
this.callBackMapping[socketType].call(this, recvData)
} else if (action === 'themeChange') {
this.callBackMapping[socketType].call(this, recvData)
}
}
}
}
// 回調(diào)函數(shù)的注冊(cè)
registerCallBack (socketType, callBack) {
this.callBackMapping[socketType] = callBack
}
// 取消某一個(gè)回調(diào)函數(shù)
unRegisterCallBack (socketType) {
this.callBackMapping[socketType] = null
}
// 發(fā)送數(shù)據(jù)的方法
send (data) {
// 判斷此時(shí)此刻有沒有連接成功
if (this.connected) {
this.sendRetryCount = 0
this.ws.send(JSON.stringify(data))
} else {
this.sendRetryCount++
setTimeout(() => {
this.send(data)
}, this.sendRetryCount * 500)
}
}
}
在main.js中進(jìn)行連接,掛載原型
//對(duì)服務(wù)端進(jìn)行連接 import SocketService from '../utils/socket_service' SocketService.Instance.connect() // 其他的組件 this.$socket Vue.prototype.$socket = SocketService.Instance
然后在組件中
created() {
//在組件創(chuàng)建完成之后進(jìn)行回調(diào)函數(shù)注冊(cè)
this.$socket.registerCallBack('trendData',this.getData)
},
mounted() {
this.initChart();
//發(fā)送數(shù)據(jù)給服務(wù)器,告訴服務(wù)器,我現(xiàn)在需要數(shù)據(jù)
this.$socket.send({
action:'getData',
socketType:'trendData',
chartName:'trend',
value:''
})
window.addEventListener("resize", this.screenAdapter);
this.screenAdapter();
},
destroyed() {
window.removeEventListener("resize", this.screenAdapter);
//取消
this.$socket.unRegisterCallBack('trendData')
},
methods:{
//res就是服務(wù)端發(fā)送給客戶端的圖表數(shù)據(jù)
getData(res) {
this.allData = res;
this.updateChart();
},
}
這樣就實(shí)現(xiàn)了后端發(fā)生變化,前端即時(shí)更新視圖
至于為什么WebSocket這樣封裝,因?yàn)楹笈_(tái)定了規(guī)則
const path = require('path')
const fileUtils = require('../utils/file_utils')
const WebSocket = require('ws')
// 創(chuàng)建WebSocket服務(wù)端的對(duì)象, 綁定的端口號(hào)是9998
const wss = new WebSocket.Server({
port: 9998
})
// 服務(wù)端開啟了監(jiān)聽
module.exports.listen = () => {
// 對(duì)客戶端的連接事件進(jìn)行監(jiān)聽
// client:代表的是客戶端的連接socket對(duì)象
wss.on('connection', client => {
console.log('有客戶端連接成功了...')
// 對(duì)客戶端的連接對(duì)象進(jìn)行message事件的監(jiān)聽
// msg: 由客戶端發(fā)給服務(wù)端的數(shù)據(jù)
client.on('message',async msg => {
console.log('客戶端發(fā)送數(shù)據(jù)給服務(wù)端了: ' + msg)
let payload = JSON.parse(msg)
const action = payload.action
if (action === 'getData') {
let filePath = '../data/' + payload.chartName + '.json'
// payload.chartName // trend seller map rank hot stock
filePath = path.join(__dirname, filePath)
const ret = await fileUtils.getFileJsonData(filePath)
// 需要在服務(wù)端獲取到數(shù)據(jù)的基礎(chǔ)之上, 增加一個(gè)data的字段
// data所對(duì)應(yīng)的值,就是某個(gè)json文件的內(nèi)容
payload.data = ret
client.send(JSON.stringify(payload))
} else {
// 原封不動(dòng)的將所接收到的數(shù)據(jù)轉(zhuǎn)發(fā)給每一個(gè)處于連接狀態(tài)的客戶端
// wss.clients // 所有客戶端的連接
wss.clients.forEach(client => {
client.send(msg)
})
}
// 由服務(wù)端往客戶端發(fā)送數(shù)據(jù)
// client.send('hello socket from backend')
})
})
}
有不懂的可以去我的github查看源代碼,前后端都有,后端必須啟動(dòng),前端才有顯示,WebSocket我只配了Trend組件,其他全部一樣的操作
github項(xiàng)目地址https://github.com/lsh555/Echarts
項(xiàng)目詳情如下

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue.js@2.6.10更新內(nèi)置錯(cuò)誤處機(jī)制Fundebug同步支持相應(yīng)錯(cuò)誤監(jiān)控
這篇文章主要介紹了Vue.js@2.6.10更新內(nèi)置錯(cuò)誤處機(jī)制,F(xiàn)undebug同步支持相應(yīng)錯(cuò)誤監(jiān)控 ,需要的朋友可以參考下2019-05-05
vue使用mpegts.js實(shí)現(xiàn)播放flv的直播視頻流
這篇文章主要為大家詳細(xì)介紹了vue如何使用mpegts.js實(shí)現(xiàn)播放flv的直播視頻流,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考一下2024-01-01
SpringBoot實(shí)現(xiàn)全局和局部跨域的兩種方式
本文主要介紹了SpringBoot實(shí)現(xiàn)全局和局部跨域的兩種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01

