需求背景
最近业务中遇到一个有意思的需求,是要在现有的表格中实现类似 Excel 中的数据条的效果,在数据比较多的时候单纯看表格里的数字会比较密集且不容易对比,加上数据条之后就比较明显的看出数据的对比情况,也让表格看起来生动了一些,这算是融合了表格和柱状图的优点。
先来看下 Excel 的效果
下面记录下实现过程和原理。
需求分析
通过图片可以看出共有几种情况:
- 只有正值:数据条全部向右
- 只有负值:数据条全部向左
- 正负值都有:正负值会以一个轴线做分割分布在左右两侧,根据正负值的多少轴线的位置也会相应的偏左或偏右
实现逻辑
实现这个效果的前提,我们要知道每列数据的最大值max和最小值min,最大值的数据条宽度就是100%,下面先用伪代码梳理下逻辑。
全是正值和全是负值的情况,这种情况就比较简单了,就是计算单元格的值占最大值的比例,计算公式是这样:数据条宽度 = (当前值 / max) * 100
正负值都有的情况多了一个“轴线位置“的计算和”数据条偏移量“计算
1
|
轴线位置 = (Math.abs(min) / (max - min)) * 100 |
1
|
数据条宽度 = (Math.abs(当前值) / (max - min)) * 100 |
1
2
3
4
5
|
// 当前值 < 0 时数据条在轴线左边,改变 right 值 // 数据条的总长为100% right = 100 - 轴线位置 // 当前值 > 0 时数据条在轴线右边,改变 left 值 left = 轴线位置 |
轴线位置逻辑其实是 "最小值到0的距离在总长度(max-min)之间的占比",我们知道数字与0之间的距离其实就是绝对值的计算,那么转换为公式来表示就是:
数据条的宽度就是 “当前值到0的距离在总长度(max-min)之间的占比”,公式表示:
- 数据条的偏移量,这里需要知道是向左还是向右偏移(最终是通过改变元素CSS的 left、right 属性来实现偏移)
完整代码实现
代码使用 Vue + ElementUI
template 部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< template > < el-table :data = "tableData" border style = "width: 100%" > < el-table-column v-for = "item in columns" sortable :key = "item.prop" :prop = "item.prop" :label = "item.label" > < template slot-scope = "scope" > <!-- 数据条 --> < div class = "data-bar" :style = "renderDataBar(item, scope.row)" ></ div > <!-- 轴线 --> < div class = "data-bar-axis" :style = "renderAxis(item, scope.row)" ></ div > <!-- 当前值 --> < span class = "cell-value" >{{ scope.row[item.prop] }}</ span > </ template > </ el-table-column > </ el-table > </ template > |
style 部分
先放 style 部分是为了让大家对基础样式有个感受,渲染函数中主要就是动态修改元素的 width、left、right 的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< style > .el-table .cell-value { position: relative; } .data-bar { position: absolute; top: 0; bottom: 0; margin: 5px auto; transition: width 0.2s; } .data-bar-axis { position: absolute; top: -1px; bottom: 0; border-right: 1px dashed #303133; } </ style > |
script 部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
<script> export default { data() { return { columns: [ { prop: 'positive' , label: '正值' , min: 1, max: 10 }, { prop: 'negative' , label: '负值' , min: -1, max: -12 }, { prop: 'mixed' , label: '正负值' , min: -6, max: 5 } ], tableData: [] } }, created() { this .initData() }, methods: { initData() { // mock数据过程,忽略 ... this .tableData.push({...}) }, renderDataBar(column, row) { const { min, max, prop } = column // 当前单元格值 const cellVal = row[prop] if (cellVal === 0) return { display: 'none' }; let style = { width: '0' , background: '#409eff' } // 全是正值 或 全是负值 if (min >= 0 || max <= 0) { const width = ((cellVal / max) * 100).toFixed(2); style.width = `${width}%` style = min >= 0 ? { ...style, left: '0' } : { ...style, right: '0' } } // 正负值都有 if (min < 0 && max > 0) { const range = max - min; // 轴线位置 const leftOffset = Math.abs((min / range) * 100) // 数据条宽度 const width = ((Math.abs(cellVal / range) * 100)).toFixed(2) style = cellVal > 0 ? { width: `${width}%`, left: `${leftOffset.toFixed(2)}%` } : { width: `${width}%`, background: '#F56C6C' , // 负值进行颜色区分 right: `${(100 - leftOffset).toFixed(2)}%` } } return style; }, renderAxis(column) { const { min, max } = column if (min < 0 && max > 0) { // 正负值都有的情况下,显示轴线 const range = max - min; const leftOffset = (((0 - min) / range) * 100).toFixed(2) return { left: `${leftOffset}%` } } else { return { display: 'none' } } } } } </script> |
最终实现效果
以上就是JS前端模拟Excel条件格式实现数据条效果的详细内容,更多关于JS模拟Excel数据条的资料请关注服务器之家其它相关文章!
原文链接:https://juejin.cn/post/7204825838537261111