话不多说了,先上一张Demo图,实现的功能有:左侧图例、右侧瀑布图、鼠标移入弹出当前坐标对应的数据信息(有优化的空间,大家自由发挥)。
图例使用到的插件
这里推荐使用安装npm插件colormap
瀑布图主体内容
这里不多做解释了,都是一些原生标签还有vue绑定的事件,可以根据实际项目情况自己封装成组件,我这里是写在一起的。
<template>
<div>
<div class="content">
<div class="neirong">
<!--图例-->
<div class="legend">
<canvas ref="legend"></canvas>
</div>
<!--瀑布图-->
<div class="waterFall" ref="waterFallContent"
@mousemove="waterFallMove($event)"
@mouseleave="waterFallLeave"
>
<canvas ref="waterFall"></canvas>
<!--鼠标移入弹出框-->
<div ref="tip" class="tip"></div>
</div>
</div>
</div>
</div>
</template>
这里是用到的Data数据
- colormap:颜色库
- legend:图例
- waterFall:瀑布图
- waterFallList:瀑布图源数据
- waterFallIndex:瀑布图定时器用到的计数标识
- waterFallCopyList:瀑布图二维数组(用来显示数据做的临时储存)
- waterFallIntervals:瀑布图定时器
- waterFallWidth:瀑布图的宽度(后端返回的数据length)
- waterFallHeight:瀑布图定高度(也可以理解成渲染次数 例如30次渲染完成)
- maxNum:图例最大值
- minNum:图例最小值
<script>
export default {
name: "index",
data() {
return {
colormap: [],
legend: null,
waterFall: null,
waterFallList: [],
waterFallIndex: 0,
waterFallCopyList: [],
waterFallIntervals: null,
waterFallWidth: 0,
waterFallHeight: 0,
maxNum: 10,
minNum: 0
}
},
下面是具体的方法,写的比较粗略,大家凑活看吧,觉得有用的大家拿走,不足之处自由发挥修改
方法调用这就不解释了,离开页面销毁定时器。
mounted() {
let dx = this
dx.setColormap()
dx.createLegendCanvas()
dx.queryChartList()
},
destroyed() {
let dx = this
clearInterval(dx.waterFallIntervals)
},
创建颜色库
这个地方具体看上面插件的官网有详细的介绍
setColormap() {
let dx = this
let colormap = require('colormap')
dx.colormap = colormap({
colormap: 'jet',
nshades: 150,
format: 'rba',
alpha: 1,
})
},
创建图例
createLegendCanvas() {
let dx = this
let legendRefs = dx.$refs.legend
dx.legend = legendRefs.getContext('2d')
let legendCanvas = document.createElement('canvas')
legendCanvas.width = 1
let legendCanvasTemporary = legendCanvas.getContext('2d')
const imageData = legendCanvasTemporary.createImageData(1, dx.colormap.length)
for (let i = 0; i < dx.colormap.length; i++) {
const color = dx.colormap[i]
imageData.data[imageData.data.length - i * 4 + 0] = color[0]
imageData.data[imageData.data.length - i * 4 + 1] = color[1]
imageData.data[imageData.data.length - i * 4 + 2] = color[2]
imageData.data[imageData.data.length - i * 4 + 3] = 255
}
legendCanvasTemporary.putImageData(imageData, 0, 0)
dx.legend.drawImage(legendCanvasTemporary.canvas,
0, 0, 1, dx.colormap.length, 50, 0, 200, dx.legend.canvas.height)
},
创建瀑布图
createWaterFallCanvas() {
let dx = this
let waterFall = dx.$refs.waterFall
dx.waterFall = waterFall.getContext('2d')
waterFall.width = dx.waterFallWidth
waterFall.height = dx.$refs.waterFallContent.offsetHeight
},
绘制单行图像
rowToImageData(data) {
let dx = this
if (dx.$refs.waterFallContent !== undefined) {
let canvasHeight = Math.floor(dx.$refs.waterFallContent.offsetHeight / dx.waterFallHeight)
let imgOld = dx.waterFall.getImageData(0, 0, dx.waterFallWidth, canvasHeight * dx.waterFallIndex + 1)
const imageData = dx.waterFall.createImageData(data.length, 1)
for (let i = 0; i < imageData.data.length; i += 4) {
const cindex = dx.colorMapData(data[i / 4], 0, 130)
const color = dx.colormap[cindex]
imageData.data[i + 0] = color[0]
imageData.data[i + 1] = color[1]
imageData.data[i + 2] = color[2]
imageData.data[i + 3] = 255
}
for (let i = 0; i < canvasHeight; i++) {
dx.waterFall.putImageData(imageData, 0, i)
}
dx.waterFall.putImageData(imgOld, 0, canvasHeight)
}
},
返回数据对应的Colormap颜色
colorMapData(data, outMin, outMax) {
let dx = this
if (data <= dx.minNum) {
return outMin
} else if (data >= dx.maxNum) {
return outMax
}
return Math.round(((data - dx.minNum) / (dx.maxNum - dx.minNum)) * outMax)
},
鼠标移入瀑布图
waterFallMove(event) {
let dx = this
let dataWidth = (dx.$refs.waterFallContent.offsetWidth / dx.waterFallWidth).toFixed(2)
let dataHeight = (dx.$refs.waterFallContent.offsetHeight / dx.waterFallHeight).toFixed(2)
let x = Math.floor(event.offsetX / dataWidth)
let y = Math.floor(event.offsetY / dataHeight)
try {
dx.$refs.tip.innerHTML = '数值:' + JSON.parse(JSON.stringify(dx.waterFallCopyList[y][x]))
let xx = event.offsetX + 5
let yy = event.offsetY - 20
if (event.offsetX > 1300) {
xx = event.offsetX - 160
yy = event.offsetY - 20
}
dx.$refs.tip.style.position = 'absolute'
dx.$refs.tip.style.left = xx + 'px'
dx.$refs.tip.style.top = yy + 'px'
dx.$refs.tip.style.display = 'block'
} catch (e) {
dx.$refs.tip.style.display = 'none'
}
},
鼠标移出瀑布图
waterFallLeave() {
let dx = this
dx.$refs.tip.style.display = 'none'
},
瀑布图假数据模拟
queryChartList() {
let dx = this
dx.waterFallWidth = 1500
dx.waterFallHeight = 30
let data = []
for (let i = 0; i < 1500; i++) {
data.push(Math.floor(Math.random() * (20 - 1)) + 1)
}
if (dx.waterFall === null) {
dx.createWaterFallCanvas(data.length)
}
dx.rowToImageData(data)
dx.waterFallCopyList.unshift(data)
dx.waterFallIndex++
if (dx.waterFallIndex > dx.waterFallHeight) {
dx.waterFallCopyList.pop()
}
dx.waterFallIntervals = setTimeout(() => {
dx.queryChartList()
}, 1000)
},
样式代码
.neirong {
width: 1800px;
height: 100%;
margin: 80px auto;
display: flex;
justify-content: center;
}
.legend {
width: 25px;
height: 500px;
}
canvas {
width: 100%;
height: 100%;
}
.waterFall {
width: 1500px;
height: 500px;
position: relative;
}
.tip {
pointer-events: none;
display: none;
background-color: #0404049e;
border-radius: 10px;
color: #fff;
padding: 10px;
box-sizing: border-box;
}
到这里这个Demo基本就是可以运行的,不会有任何报错,代码写的不是很高级,我本人也是个初级的小菜鸟,也是第一次写文章,希望大佬可以给出一些更好的建议我也会好好学习的,也希望那些遇到类似这个需求没什么思路的小伙伴可以借鉴我的踩坑之旅,可以更快的成长起来。
到此这篇关于vue+canvas实现数据实时从上到下刷新瀑布图效果(类似QT的)的文章就介绍到这了,更多相关vue+canvas实时刷新瀑布图 内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论