Vue+canvas realizes the effect of refreshing waterfall chart from top to bottom in real time (similar to QT)

Vue+canvas realizes the effect of refreshing waterfall chart from top to bottom in real time (similar to QT)

Without further ado, here is a demo picture. The functions implemented are: legend on the left, waterfall chart on the right, and data information corresponding to the current coordinates popping up when the mouse is moved (there is room for optimization, please feel free to play with it).

Plugins used in the illustration

It is recommended to install the npm plugin colormap

Waterfall chart body

I won’t explain it here. They are all native tags and vue-bound events. You can encapsulate them into components according to the actual project situation. I have written them together here.

<template>
    <div>
        <div class="content">
            <div class="neirong">
                <!--Legend-->
                <div class="legend">
                    <canvas ref="legend"></canvas>
                </div>
                <!--Waterfall Chart-->
                <div class="waterFall" ref="waterFallContent"
                     @mousemove="waterFallMove($event)"
                     @mouseleave="waterFallLeave"
                >
                    <canvas ref="waterFall"></canvas>
                    <!--Move the mouse into the pop-up box-->
                    <div ref="tip" class="tip"></div>
                </div>
            </div>
        </div>
    </div>
</template>

Here is the Data used

  • colormap: color library
  • legend: legend
  • waterFall: Waterfall Chart
  • waterFallList: waterfall chart source data
  • waterFallIndex: The counting index used by the waterfall chart timer
  • waterFallCopyList: waterfall chart two-dimensional array (used for temporary storage of display data)
  • waterFallIntervals: waterfall timer
  • waterFallWidth: width of the waterfall chart (length of data returned by the backend)
  • waterFallHeight: The height of the waterfall (can also be understood as the number of renderings, for example, 30 renderings are completed)
  • maxNum: maximum value of the legend
  • minNum: minimum value of the legend
<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
            }
        },

The following is a specific method. It is written roughly. Please read it carefully. If you find it useful, please take it away. If there are any shortcomings, you can modify it freely.

I won't explain the method call here, leaving the page will destroy the timer.

mounted() {
            let dx = this
            dx.setColormap()
            dx.createLegendCanvas()
            dx.queryChartList()
        },
        destroyed() {
            let dx = this
            clearInterval(dx.waterFallIntervals)
        },

Create a color library

For details, please refer to the official website of the above plug-in for detailed introduction.

setColormap() {
      let dx = this
      let colormap = require('colormap')
      dx.colormap = colormap({
          colormap: 'jet',
          nshades: 150,
          format: 'rba',
          alpha: 1,
   })
},

Creating a Legend

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)
            },

Creating a waterfall chart

 createWaterFallCanvas() {
                let dx = this
                let waterFall = dx.$refs.waterFall
                dx.waterFall = waterFall.getContext('2d')
                waterFall.width = dx.waterFallWidth
                waterFall.height = dx.$refs.waterFallContent.offsetHeight
            },

Draw a single line image

 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)
                }
            },

Returns the Colormap color corresponding to the data

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)
            },

Move the mouse into the waterfall chart

            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 = 'Value:' + 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'
                }
            },

Move the mouse out of the waterfall chart

waterFallLeave() {
                let dx = this
                dx.$refs.tip.style.display = 'none'
            },

Waterfall chart fake data simulation

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)
            },

Style Code

.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;
    }

At this point, the Demo can basically run without any errors. The code is not very advanced. I am also a beginner and this is my first time writing an article. I hope the big guys can give me some better suggestions and I will study hard. I also hope that friends who encounter similar requirements and have no ideas can learn from my experience of stepping into the pit and grow faster.

This is the end of this article about how vue+canvas achieves real-time data refresh from top to bottom waterfall chart effect (similar to QT). For more related vue+canvas real-time refresh waterfall chart content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed explanation of the implementation principle of Vue2.0/3.0 two-way data binding
  • Analysis on the problem of data loss caused by forced refresh of vuex
  • How does Vue track data changes?
  • Solution to Vue data assignment problem
  • Vue resets data to its initial state
  • Analysis and solution of data loss during Vue component value transfer
  • SpringBoot+Vue realizes data adding function
  • Handwritten Vue2.0 data hijacking example
  • Vue data two-way binding implementation method
  • Avoid abusing this to read data in data in Vue
  • Design a data collector with vue

<<:  Detailed explanation of how to solve the conflict of project URLs caused by setting the default path of Tomcat

>>:  Mysql 5.7.18 Using MySQL proxies_priv to implement similar user group management

Recommend

React passes parameters in several ways

Table of contents Passing parameters between pare...

JS calculates the probability of winning based on the prize weight

Table of contents 1. Example scenario 1.1. Set th...

Solution to "Specialized key was too long" in MySQL

Table of contents Solution 1 Solution 2 When crea...

Introduction to Linux compression and decompression commands

Table of contents Common compression formats: gz ...

Several principles for website product design reference

The following analysis is about product design pr...

Practice of using Tinymce rich text to customize toolbar buttons in Vue

Table of contents Install tinymce, tinymce ts, ti...

FastDFS and Nginx integration to achieve code analysis

FastDFS & Nginx Integration: The tracker is c...

Linux completely removes node.js and reinstalls it through the yum command

first step Delete it once with the built-in packa...

Json advantages and disadvantages and usage introduction

Table of contents 1. What is JSON 1.1 Array liter...

WeChat applet learning wxs usage tutorial

What is wxs? wxs (WeiXin Script) is a scripting l...

Summary of CSS usage tips

Recently, I started upgrading my blog. In the proc...

JavaScript to implement a simple shopping form

This article shares the specific code of JavaScri...

JavaScript to achieve progress bar effect

This article example shares the specific code of ...

Cause Analysis and Solution of I/O Error When Deleting MySQL Table

Problem phenomenon I recently used sysbench to te...

JS 4 super practical tips to improve development efficiency

Table of contents 1. Short circuit judgment 2. Op...