使用 React Hook 高效实现动态炫酷的可视化图表
2012 年 5 月 3 日
function getRandomNumber(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }; function generateData(iterations = 100, defaultValues = [], namePrefix = {}, maxJump = 100) { const arr = []; for (let i = 0; i { if (i === 0 && typeof v.value === 'number') { return v; } return { ...v, value: i === 0 ? this.getRandomNumber(1, 1000) : arr[i - 1].values[idx].value + this.getRandomNumber(0, maxJump) } }); arr.push({ name: `${namePrefix.prefix || ''} ${(namePrefix.initialValue || 0) + i}`, values }); } return arr; }; export default { getRandomNumber, generateData }
mocks.js
:
import helpers from './helpers'; const defaultChart = [ { id: 1, label: 'Google', value: helpers.getRandomNumber(0, 50) }, { id: 2, label: 'Facebook', value: helpers.getRandomNumber(0, 50) }, { id: 3, label: 'Outbrain', value: helpers.getRandomNumber(0, 50) }, { id: 4, label: 'Apple', value: helpers.getRandomNumber(0, 50) }, { id: 5, label: 'Amazon', value: helpers.getRandomNumber(0, 50) }, ]; export default { defaultChart, }
一个乞丐版的动态排行榜可视化就做好喇。

8. 完整代码
import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import './styles.scss'; const getRandomColor = () => { const letters = '0123456789ABCDEF'; let color = '#'; for (let i = 0; i { return `translateY(${value}px)`; } const DynamicBarChart = (props) => { const [dataQueue, setDataQueue] = useState([]); const [activeItemIdx, setActiveItemIdx] = useState(0); const [highestValue, setHighestValue] = useState(0); const [currentValues, setCurrentValues] = useState({}); const [firstRun, setFirstRun] = useState(false); let iterationTimeoutHolder = null; function start () { if (activeItemIdx > 1) { return; } nextStep(true); } function setNextValues () { if (!dataQueue[activeItemIdx]) { iterationTimeoutHolder = null; return; } const roundData = dataQueue[activeItemIdx].values; const nextValues = {}; let highestValue = 0; roundData.map((c) => { nextValues[c.id] = { ...c, color: c.color || (currentValues[c.id] || {}).color || getRandomColor() }; if (Math.abs(c.value) > highestValue) { highestValue = Math.abs(c.value); } return c; }); console.table(highestValue); setCurrentValues(nextValues); setHighestValue(highestValue); setActiveItemIdx(activeItemIdx + 1); } function nextStep (firstRun = false) { setFirstRun(firstRun); setNextValues(); } useEffect(() => { setDataQueue(props.data); }, []); useEffect(() => { start(); }, [dataQueue]); useEffect(() => { iterationTimeoutHolder = window.setTimeout(nextStep, 1000); return () => { if (iterationTimeoutHolder) { window.clearTimeout(iterationTimeoutHolder); } }; }, [activeItemIdx]); const keys = Object.keys(currentValues); const { barGapSize, barHeight, showTitle, data } = props; console.table('data', data); const maxValue = highestValue / 0.85; const sortedCurrentValues = keys.sort((a, b) => currentValues[b].value - currentValues[a].value); const currentItem = dataQueue[activeItemIdx - 1] || {}; return ({ { showTitle &&); }; DynamicBarChart.propTypes = { showTitle: PropTypes.bool, iterationTimeout: PropTypes.number, data: PropTypes.array, startRunningTimeout: PropTypes.number, barHeight: PropTypes.number, barGapSize: PropTypes.number, baseline: PropTypes.number, }; DynamicBarChart.defaultProps = { showTitle: true, iterationTimeout: 200, data: [], startRunningTimeout: 0, barHeight: 50, barGapSize: 20, baseline: null, }; export { DynamicBarChart };{currentItem.name}
}} { sortedCurrentValues.map((key, idx) => { const currentValueData = currentValues[key]; const value = currentValueData.value let width = Math.abs((value / maxValue * 100)); let widthStr; if (isNaN(width) || !width) { widthStr = '1px'; } else { widthStr = `${width}%`; } return ({currentValueData.value}); }) }
styles.scss
.live-chart { width: 100%; padding: 20px; box-sizing: border-box; position: relative; text-align: center; h1 { font-weight: 700; font-size: 60px; text-transform: uppercase; text-align: center; padding: 20px 10px; margin: 0; } .chart { position: relative; margin: 20px auto; } .chart-bars { position: relative; width: 100%; } .bar-wrapper { display: flex; flex-wrap: wrap; align-items: center; position: absolute; top: 0; left: 0; transform: translateY(0); transition: transform 0.5s linear; padding-left: 200px; box-sizing: border-box; width: 100%; justify-content: flex-start; label { position: absolute; height: 100%; width: 200px; left: 0; padding: 0 10px; box-sizing: border-box; text-align: right; top: 50%; transform: translateY(-50%); font-size: 16px; font-weight: 700; display: flex; justify-content: flex-end; align-items: center; } .value { font-size: 16px; font-weight: 700; margin-left: 10px; } .bar { width: 0%; transition: width 0.5s linear; } } }
原项目地址:react-dynamic-charts:https://dsternlicht.github.io/react-dynamic-charts/

结语
一直对实现动态排行榜可视化感兴趣,无奈多数都是基于 D3
或 echarts
实现。
而这个库,不仅脱离图形库,还使用了 React 16
的新特性。也让我彻底理解了 React Hook
的妙用。

:heart: 看完三件事
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
-
点个「 在看 」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
-
关注我的官网 https:// m uyiy.cn ,让我们成为长期关系
-
关注公众号「 高级前端进阶 」,每周重点攻克一个前端面试重难点,公众号后台回复「面试题」 送你高级前端面试题。