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

Steps for docker container exit error code

Sometimes some docker containers exit after a per...

How to separate static and dynamic state by combining Apache with Tomcat

Experimental environment Apache and Tomcat are bo...

Solve the problem of MySql client exiting in seconds (my.ini not found)

Problem description (environment: windows7, MySql...

js to realize the function of uploading pictures

The principle of uploading pictures on the front ...

Introduction to query commands for MySQL stored procedures

As shown below: select name from mysql.proc where...

Complete steps to install Anaconda3 in Ubuntu environment

Table of contents Introduction to Anaconda 1. Dow...

Detailed explanation of CSS background and border tag examples

1. CSS background tag 1. Set the background color...

Some parameter descriptions of text input boxes in web design

<br />In general guestbooks, forums and othe...

Linux redis-Sentinel configuration details

download Download address: https://redis.io/downl...

How to implement nested if method in nginx

Nginx does not support nested if statements, nor ...

Solution for Docker container not recognizing fonts such as Songti

Problem background: When using docker to deploy t...

Detailed explanation of Mybatis special character processing

Preface: Mybatis special character processing, pr...

How to assign a public IP address to an instance in Linux

describe When calling this interface, you need to...