Vue使用Echarts实现横向柱状图,并通过WebSocket即时通讯更新
作者:进阶的巨人001
这篇文章主要介绍了Vue使用Echarts实现横向柱状图,并通过WebSocket即时通讯更新方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
先看下效果图
并且数据每隔3秒自动变换一次
先看下后台返回的数据结构是什么样子的
[ { "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" } ]
开始实现前端的代码
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对象 allData: null, //接收的后台数据 currentPage: 1, //当前显示的页数 totalPage: 0, //一共有多少页 timerId: null //定时器标识 } },
methods
initChart方法
initChart() { //初始化echartInstance对象 //chalk是我们定义的主题,echarts官方有案例,怎么使用可以百度一下,不喜欢可以直接删掉 this.chartInstance = this.$echarts.init(this.$refs.seller_ref, 'chalk') //对图表初始化配置对控制 const initOption = { title: { text: '▎商家销售统计', left: 20, top: 20 }, grid: { top: '20%', left: '3%', right: '6%', bottom: '3%', containLabel: true // 距离是包含坐标轴上的文字 }, 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状态之下的颜色值 { offset: 0, color: '#5052EE' }, // 百分之100状态之下的颜色值 { offset: 1, color: '#AB6EE5' } ]) } }] } this.chartInstance.setOption(initOption) //对图表对象进行鼠标事件监听 //鼠标移入,定时器停止 this.chartInstance.on('mouseover', () => { clearInterval(this.timerId) }) //鼠标移出,定时器开始 this.chartInstance.on('mouseout', () => { this.startInterval() }) },
getData方法
这里还是用http请求获取的数据,后面我再讲怎么用WebSocket获取我们的数据
async getData() { const { data: res } = await this.$http.get('seller') this.allData = res //对数据进行排序 this.allData.sort((a, b) => { return a.value - b.value //从小到大排序 }) //每五个元素显示一页 this.totalPage = this.allData.length % 5 === 0 ? this.allData.length / 5 : this.allData.length / 5 + 1 this.updateChart() //启动定时器 this.startInterval() },
updateChart方法
//更新图表 updateChart() { //起始的位置 const start = (this.currentPage - 1) * 5 //结束的位置 //起始为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方法
//定时器,数据每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) //手动调用图表对象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获取数据说一下
封装了一个WebSocket
export default class SocketService { /** * 单例 */ static instance = null static get Instance() { if (!this.instance) { this.instance = new SocketService() } return this.instance } // 和服务端连接的socket对象 ws = null // 存储回调函数 callBackMapping = {} // 标识是否连接成功 connected = false // 记录重试的次数 sendRetryCount = 0 // 重新连接尝试的次数 connectRetryCount = 0 // 定义连接服务器的方法 connect() { // 连接服务器 if (!window.WebSocket) { return console.log('您的浏览器不支持WebSocket') } this.ws = new WebSocket('ws://localhost:9998') // 连接成功的事件 this.ws.onopen = () => { console.log('连接服务端成功了') this.connected = true // 重置重新连接的次数 this.connectRetryCount = 0 } // 1.连接服务端失败 // 2.当连接成功之后, 服务器关闭的情况 this.ws.onclose = () => { console.log('连接服务端失败') this.connected = false this.connectRetryCount++ setTimeout(() => { this.connect() }, 500 * this.connectRetryCount) } // 得到服务端发送过来的数据 this.ws.onmessage = msg => { console.log('从服务端获取到了数据') // 真正服务端发送过来的原始数据时在msg中的data字段 // console.log(msg.data) const recvData = JSON.parse(msg.data) const socketType = recvData.socketType // 判断回调函数是否存在 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) } } } } // 回调函数的注册 registerCallBack (socketType, callBack) { this.callBackMapping[socketType] = callBack } // 取消某一个回调函数 unRegisterCallBack (socketType) { this.callBackMapping[socketType] = null } // 发送数据的方法 send (data) { // 判断此时此刻有没有连接成功 if (this.connected) { this.sendRetryCount = 0 this.ws.send(JSON.stringify(data)) } else { this.sendRetryCount++ setTimeout(() => { this.send(data) }, this.sendRetryCount * 500) } } }
在main.js中进行连接,挂载原型
//对服务端进行连接 import SocketService from '../utils/socket_service' SocketService.Instance.connect() // 其他的组件 this.$socket Vue.prototype.$socket = SocketService.Instance
然后在组件中
created() { //在组件创建完成之后进行回调函数注册 this.$socket.registerCallBack('trendData',this.getData) }, mounted() { this.initChart(); //发送数据给服务器,告诉服务器,我现在需要数据 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就是服务端发送给客户端的图表数据 getData(res) { this.allData = res; this.updateChart(); }, }
这样就实现了后端发生变化,前端即时更新视图
至于为什么WebSocket这样封装,因为后台定了规则
const path = require('path') const fileUtils = require('../utils/file_utils') const WebSocket = require('ws') // 创建WebSocket服务端的对象, 绑定的端口号是9998 const wss = new WebSocket.Server({ port: 9998 }) // 服务端开启了监听 module.exports.listen = () => { // 对客户端的连接事件进行监听 // client:代表的是客户端的连接socket对象 wss.on('connection', client => { console.log('有客户端连接成功了...') // 对客户端的连接对象进行message事件的监听 // msg: 由客户端发给服务端的数据 client.on('message',async msg => { console.log('客户端发送数据给服务端了: ' + 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) // 需要在服务端获取到数据的基础之上, 增加一个data的字段 // data所对应的值,就是某个json文件的内容 payload.data = ret client.send(JSON.stringify(payload)) } else { // 原封不动的将所接收到的数据转发给每一个处于连接状态的客户端 // wss.clients // 所有客户端的连接 wss.clients.forEach(client => { client.send(msg) }) } // 由服务端往客户端发送数据 // client.send('hello socket from backend') }) }) }
有不懂的可以去我的github查看源代码,前后端都有,后端必须启动,前端才有显示,WebSocket我只配了Trend组件,其他全部一样的操作
github项目地址https://github.com/lsh555/Echarts
项目详情如下
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。