动态刻度可视化组件实现

拈花ヽ惹草 提交于 2021-02-16 13:41:28

前言

之前有个网友问我了一个很有价值的问题, 有关实现数据可视化的问题, 但是这个可视化问题不是一般的柱状图, 折现图之类的,而是不规则刻度的数据可视化.所以笔者思考了一下决定自己实现一个动态刻度可视化组件的方案, 来解决这一类的需求.

正文

最初的需求是这样的:

我们只需要输入文字, 数值比例, 就能生成如上图所示的刻度图.但是作为一名有追求的程序员, 需要对问题抽象化, 形成通用的解决方案,所以我们开始重组需求:

由上图我们可以拆解为一下几个需求点:
  • 支持数值自定义
  • 数值单位自定义
  • 支持刻度组件宽度自定义
  • 支持刻度线数量自定义
  • 支持刻度变化幅度自定义
  • 传入已有进度比例,即激活区范围
  • 支持刻度样式自定义
  • 支持数值样式自定义
  • 支持自定义说明文本以及说明文本自定义
以上就是笔者挖掘的通用需求,当然有其他需求也可以渐进的增加.

确认了以上需求之后,我们开始选择技术选型, 笔者之前常用的技术栈是vuereact,所以接下来我们初步确认该组件采用如下技术方案:

  • react + typescript + umi-library

如果大家擅长使用vue, 也可以, 笔者之前也写过如何搭建vue组件库相关的文章,感兴趣可以学习了解一下, 其本质思想是一致的.

接下来我们开始实现动态刻度可视化组件. 如果对umi不熟悉的,可以参考笔者之前写的文章从0到1教你搭建前端团队的组件系统(高级进阶必备).

1. 定义基本属性类型

由以上需求分析我们可以定义如下的属性类型:

   
   
   
export interface TickerProps {
width: number;
maxHeight: number;
percent: number;
text: string;
value: number;
showValue: boolean;
unit: string;
lineNum: number;
defaultColor: string;
activeColor: string;
textStyle: object;
valueStyle: object;
}

复制代码

2. 组件整体结构


   
   
   
const Ticker: React.FC<TickerProps> = function(props:TickerProps) {
const {
width = 100,
maxHeight = 10,
percent = 50,
value,
text = '瞬时能见度',
showValue = true,
unit = 'M',
lineNum = 12,
defaultColor = '#06c',
activeColor = 'red',
valueStyle,
textStyle
} = props
return (
<div className="ticker">
{
showValue &&
<div className="value" style={valueStyle}>
{ value || 0 } <span className="unit">{ unit }</span>
</div>
}
<div className="tickerGraph">
<div className="tickerLine">

</div>
<div className="tickerBar"></div>
</div>
{
!!text && <div className="text">{ text }</div>
}
</div>
);
};

export default Ticker;

复制代码

3. 视图搭建

有关刻度可视化我们完全采用dom实现, 所以这里笔者具体分析一下如何实现刻度视图:

上图的思路就是用一个个dom来组装成随机刻度图形,所以我们只要利用css实现这个形状, 也就成功了一半.至于激活状态,我们会根据传入的数据量来决定激活范围,接下来会介绍如何渲染激活的刻度,也就是上图的红色区域.

4.特殊功能实现

因为该组件很多功能在搭建结构之后已经实现了, 这里我们唯一关注的就是cssjs长度计算的问题, css实现方案有很多, 这里就不具体介绍了, 笔者这里重点介绍一下如何实现指定范围的随机高度:


   
   
   
// 生成指定范围的随机高度
const random = (min:number, max:number):number => {
return min + Math.random() * (max - min)
}

复制代码

动态刻度条的随机高度我们就是利用以上函数实现的, 刻度条内部实现如下:


   
   
   
<div className="tickerLine" style={{borderBottomColor: defaultColor}}>
{
new Array(lineNum).fill(0).map((item:number, i: number) => {
let isActive = (i + 1) <= Math.floor(lineNum * percent / 100)
return <span
className="tick"
style={{
height: random(3, maxHeight) + 'px', left: (gap + 2) * i + 'px',
backgroundColor: isActive ? activeColor : defaultColor
}}>
</span>
})
}
</div>
复制代码

gap为刻度之间的间距, 由于计算刻度的位置需要一点几何知识, 公式如下:


   
   
   
W = Lw * lineNum + gap * ( lineNum - 1)
复制代码
其中 W表示刻度总宽度,  Lw为刻度线宽度,  lineNum为刻度线数量.

还有一个注意点就是激活态, 笔者使用如下函数来判断刻度是否具有激活状态:


   
   
   
let isActive = (i + 1) <= Math.floor(lineNum * percent / 100)
复制代码

这块也非常好理解, 也就是我们传入的比率乘以线的总数量,即可求出哪些刻度线是需要激活的.

以上细节实现完成之后,我们就可以来实现有点意思的刻度可视化方案啦, 如下展示的demo:


  1. 可见度测量


    1. 正态分布模型


    1. 标尺


    1. 光栅


    1. 自定义文本样式

    github地址

    https://github.com/MrXujiang/ticker

    最后

    如果想学习更多H5游戏webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。

    本文分享自微信公众号 - 趣谈前端(beautifulFront)。
    如有侵权,请联系 support@oschina.cn 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
    该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!