Node.js + express + react + echarts 從零搭建數據可視化平臺

最近負責搭建公司大屏可視化平臺,前端用到 vue + echarts ,後端 java 以及 大數據 提供數據支持。過程中踩過許多坑,於是準備在項目上線後,自己搭建響應式數據可視化平臺。

技術棧

  • react
  • node.js

第三方插件

  • express 中間件
  • echarts 圖表
  • Socket.io 服務
  • WOW.js 動畫

資源連接:https://github.com/zhangyongwnag/screen-visual
Demo:https://zhangyongwnag.github.io/screen-visual/build/index.html

客戶端

一、初始化

create-react-app screen-visual

二、安裝插件

npm i echarts react-countup wowjs react-transition-group -S
yarn add echarts react-countup wowjs react-transition-group

三、靜態頁面

因爲我們最後要做成響應式佈局,所以編寫css要規範化,考慮自適應

我們決定網頁的整體佈局採用傳統的聖盃佈局,倆邊固定,中間自適應

基礎默認樣式:這裏由於用到很多transform動畫,動畫觸發間接瀏覽器迴流重繪,考慮到性能問題,這裏開啓硬件加速

* {
  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微軟雅黑", Arial, sans-serif !important;
  padding: 0;
  margin: 0;
  user-select: none; /*禁止選中*/
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  cursor: pointer; /*鼠標指針*/
  box-sizing: border-box; /*怪異盒模型*/
  font-size: 18px;
  transform: translateZ(0); /*硬件加速*/
  -webkit-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -o-transform: translateZ(0);
  -ms-transform: translateZ(0);
}

考慮到自適應,我們把倆邊也設爲百分比

整體的佈局:左邊24%,中間40%,右邊36%

/*最外層包裹器*/
.wrap {
  width: 100vw;
  min-width: 360px;
  height: 100vh;
  overflow: hidden;
  /*filter: blur(5px);*/
  background-color: #000;
  display: flex;
  justify-content: space-evenly;
  padding: 0.5%;
  transition: all 0.5s;
}

/*倆邊*/
.section_side {
  width: 24%;
  height: 100%;
  padding-bottom: 0.5%;
  transition: all 0.5s;
  background-color: rgba(32, 43, 71, 0.6);
  -webkit-transition: border linear .2s, -webkit-box-shadow linear .5s;
  border-color: rgba(141, 39, 142, .75);
  -webkit-box-shadow: 0 0 7px rgba(87, 147, 243, 1);
}

/*中間*/
.section_middle {
  width: 40%;
  height: 100%;
  margin: 0 0.5%;
  padding-bottom: 0.5%;
  transition: all 0.5s;
  background-color: rgba(32, 43, 71, 0.6);
  -webkit-transition: border linear .2s, -webkit-box-shadow linear .5s;
  border-color: rgba(141, 39, 142, .75);
  -webkit-box-shadow: 0 0 7px rgba(87, 147, 243, 1);
}

佈局

return (
	<div className='wrap'>
	       <div className='section_side'></div>
	       <div className='section_middle'></div>
	       <div className='section_side' style={{width: '36%'}}></div>
	</div>
)

我們看到基本的佈局效果:
在這裏插入圖片描述
接下來,我們把不同的echarts圖表插入到頁面
在這裏插入圖片描述
插入不用的圖表,經過這一步,我想你對echarts的圖表會有一個更深的體會,更熟練的應用(數據都是模擬)


走到這裏,會發現一個問題,切換視口大小時,echarts圖表不會自適應切換,我們查閱文檔,發現官方提供了對應的方法:resize()

於是我們在每個echarts繪製完後,主動調用echarts.resize()方法,發現他只會在繪製的時候生效,切換視口還是不會自適應
在這裏插入圖片描述

於是找度娘,度娘給出的意見:

在每個echarts圖表繪製完後,監聽窗口resize,隨之調用resize()方法
在這裏插入圖片描述
方法的確有效果,但是我們每繪製一個echarts圖表,就要監聽窗口resize,代碼重複

我們嘗試在頁面初始化監聽窗口resize,並且把繪製的echarts維護到全局的變量,當窗口大小發生變化,我們就遍歷調用resize()

import React, {Component} from 'react';
import echarts from 'echarts'

export default class App extends Component {
  constructor() {
    super()
    this.state = {
      echartsList: [], // echarts繪製的結果
    }
  }

componentDidMount() {
  // 監聽窗口變化
  window.addEventListener('resize', this.resizeEcharts)
}

// 繪製某個echarts圖表
renderUserInfoEcharts = () => {
	let dom = document.getElementById('user_info')
	let eCharts = echarts.init(dom)
	let option = [
		...
	]
	eCharts.setOption(option, true)
	let echartsList = [...this.state.echartsList, eCharts]
	this.setState({
		echartsList 
	})
}

// 重置echarts的大小
resizeEcharts = () => {
    this.state.echartsList.map(item => {
      setTimeout(() => {
        item.resize()
      }, 200)
    })
}

根據上面的方式,我們在繪製圖表時配置一遍,配置完後。我們可以看到效果圖,完美自適應 ( gif製作工具沒充錢,看起來比較卡,實際不卡 )
在這裏插入圖片描述

響應式

PC佈局我們已經寫好了,接下來,我們加幾個斷點製作一個簡單地響應式佈局

基準斷點:
< 1499px
< 1199px
< 967px
< 748px
< 479px
< 1499px
@media screen and (max-width: 1499px) {
	...
}

在這裏插入圖片描述

< 1199px
@media screen and (max-width: 1199px) {
	...
}

在這裏插入圖片描述

< 967px
@media screen and (max-width: 967px) {
	...
}

在這裏插入圖片描述

< 748px
@media screen and (max-width: 748px) {
	...
}

在這裏插入圖片描述

< 479px
@media screen and (max-width: 479px) {
	...
}

在這裏插入圖片描述
四、動畫效果

靜態頁面我們已經完成了,接下來,我們給網頁加些動畫效果

①:首次Loading加載效果

首先,我們在初始化加載頁面時,加載一個loading動畫,這裏我們寫一個Loading加載的組件

import React, {Component} from 'react'
import '../asset/css/Loading.css'

export default class Loading extends Component {
  constructor(props){
    super(props)
  }
  render () {
    return (
      <div style={{width:'100vw',height:'100vh',backgroundColor:'#2a2a2a'}} className='common_flex'>
        <div className='loader'>Loading...</div>
      </div>
    )
  }
}

接着在App.js引入使用,

import React, {Component} from 'react';
import Loading from ' ./components/Loading.'

export default class App extends Component {
 constructor() {
 	super()
 	this.state = {
 		loading: true, // loading加載標識,默認加載中
 	}
 }
 componentDidMount() {
 	// 這裏模擬加載一秒,後面我們寫了服務端後,根據數據返回情況按需加載
 	setTimeout(() => {
 		this.setState({
 			loading: false
 		}, () => {
 			// 當loading狀態發生變化立即執行
 		})
 	}, 1000)
 }
 render() {
 	let { loading } = this.state
 	if (loading) {
 		return (
 			<Loading/>
 		)
 	}else {
	 	return (
 			<div>主內容</div>
 		)
 	}
  }
}

Loading加載動畫基本實現
在這裏插入圖片描述
②:元素滾動動畫效果

接下來,我們給頁面的每個元素加滾動動畫,這裏我們用到 wow.js 動畫庫

wowjs 動畫庫依賴於 animate.css 所以大部分的css都可以直接拿來使用

前面已經下載過了,直接在我們需要用到的組件引入 wowjs

import React, {Component} from 'react';
import {WOW} from ' wowjs'

export default class App extends Component {
  constructor() {
  	super()
  }
  componentDidMount() {
  	let wow = new WOW({
	    boxClass: 'wow',
	    animateClass: 'animated',
	    offset: 0,
	    mobile: true,
	    live: true
	});
	wow.init();
 }
 render() {
 	return (
 		<div className=‘wow slideInLeft’ data-wow-duration="2s" data-wow-delay="5s" data-wow-offset="10" data-wow-iteration="10">動畫效果</div>
 	)
}

自定義配置

屬性/方法 類型 默認值 說明
boxClass String wow 需要執行動畫的元素的 class
animateClass String animated animation.css 動畫的 class
offset Number 0 距離可視區域多少開始執行動畫
mobile Boolean true 是否在移動端執行動畫
live Boolean true 異步加載的內容是否有效

標籤屬性配置

屬性 說明
data-wow-duration=“2s” 執行動畫所需要的時間
data-wow-delay=“2s” 執行動畫延遲執行的時間
data-wow-offset=“0” 距頂部多少開始執行動畫
data-wow-iteration=“infinity” 執行動畫的次數 infinity無限

在這裏插入圖片描述
③:數字滾動效果

我們利用 react-countup 插件實現數字滾動效果,他是依賴於 countup 輕量級插件

前面已經下載,直接在所需要的組件引入使用

import React, {Component} from 'react';
import countUp from 'react-countup'

export default class App extends Component {
  constructor() {
  	super()
  }
 render() {
 	return (
 		<CountUp start={0} end={parseInt(Math.random() * 10000)} suffix=' 個' duration={4} separator=','/>
 	)
}

屬性配置

屬性 類型 說明
start Number 開始的值
end Number 結束的值
suffix String 單位
duration Number 動畫執行時間
separator String 分隔符

在這裏插入圖片描述

④:列表插入效果

之前的文章我們有介紹 react-transition-group 官方提供的動畫庫,這裏就不過多介紹了

我們直接介紹用法

import React, {Component} from 'react'
import {TransitionGroup, CSSTransition} from 'react-transition-group'

export default class Table extends Component {
  constructor(props) {
    super(props)
    this.state = {
    	tableList:['1','2','3','4']
    }
  }
  render() {
  	let { tableList } = this.state
  	return (
       <TransitionGroup>
           {
                tableList.map((item, index) => (
                  <CSSTransition
                    key={index}
                    timeout={1000} //動畫執行1秒
                    classNames='fade' //自定義的class名
                  >
                  	<div>{item}</div>
                  </CSSTransition>
                ))
            }
        </TransitionGroup>
  	)
  }
}

在這裏插入圖片描述
這裏的滾動效果,利用 transform: translateY(value)

客戶端OK,接下來我們寫一個簡單的服務端


服務端

我們利用express中間件搭配socket.io建立通信

一、安裝

npm i express socket.io -S

二、搭建

socket服務配合http服務一起使用,所以必須要http監聽

let express = require('express')
let app = express()
let http = require('http').Server(app)
let io = require('socket.io')(http)

//設置CORS
app.all('*',function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Credentials','true');
  next();
});

app.get('/', (req, res) => res.send('Hello'))

// 建立socket連接
io.on('connection', socket => {
	// 發送消息標識open
   socket.emit('open', '初始化連接')
   // 當有用戶關閉時,全體廣播
   socket.on('disconnect', () => {
     socket.broadcast.emit('關閉')
   })
})

// 這裏必須http監聽 否則客戶端會報404
let server = http.listen(8888, '127.0.0.1', () => {
  let host = server.address().address
  let port = server.address().port

  console.log(`Server running at http://${host}:${port}`)
})

我們啓動服務端 node serve.js,可以看到,正常啓動
在這裏插入圖片描述
訪問 http://127.0.0.1:8888,可以看到,啓動成功了
在這裏插入圖片描述


!!! 接下來,與客戶端交互 !!!

客戶端安裝:npm i socket.io-client -S

import React, {Component} from 'react';

// 引入 socket.io-client 並建立連接 http://127.0.0.1:8888 即爲node啓動的服務器地址
let socket = require('socket.io-client')('http://127.0.0.1:8888')

export default class App extends Component {
  constructor() {
  	super()
  }
  componentDidMount() {
  	// 客戶端用on接受消息,open是服務端設置的標識
    socket.on('open', data => {
      // 如果是首次加載
      if (this.state.loading) {
        this.setState({
          loading: false
        }, () => {
          // 渲染數據
          ...
        })
      }else {
        // 渲染數據
        ...
      }
    })
 }
 render() {
 	return (
 		<div>socket.io-client 測試</div>
 	)
}

在這裏插入圖片描述
前後端交互基本完畢,socket 通信


相關資源

相關文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章