背景
在echarts没发现有可以直接使用的展示百分比的柱形图,只好自己封装一个组件使用
实现思路
一、图形拆解
要实现的组件是一个 可配置的圆柱形液柱图组件,常用于展示比例进度,比如任务完成度、指标达成率等。把图拆成最小单元然后拼接起来,如下表:
功能点 | 描述 |
---|---|
1. 显示进度柱 | 高度随 num / target 变化的水柱视觉效果 |
2. 圆柱外形 | 顶部、底部使用椭圆(border-radius)构造立体感 |
3. 自定义样式 | 宽高、颜色可配置 |
4. 百分比显示 | 可选是否显示进度数值 |
5. 动画过渡 | 数值变化时水柱高度平滑变化 |
二、结构设计
cpp
<div class="lui-column-bg"> <!-- 圆柱容器 -->
<div class="lui-inner"> <!-- 动态高度水柱 -->
<div class="lui-inner-text"> <!-- 可选百分比文字 -->
{{ validPercentage }}%
</div>
</div>
</div>
.lui-column-bg
是整个圆柱容器,带有顶部/底部的圆帽(伪元素::before/::after
).lui-inner
是内部水柱块,其height
是根据 num/target 实时计算的.lui-inner-text
是可选显示的百分比数值
三、 计算逻辑
cpp
validPercentage() {
if (this.target <= 0) return 0;
const percent = (this.num / this.target) * 100;
return Math.min(Math.round(percent), 100);
}
- 避免除以 0 的错误
- 保证水柱最大不超过 100%
Math.round
取整更简洁美观(页面不出现小数点)
四、关键样式
1. .lui-column-bg
cpp
.lui-column-bg {
background-color: #2a4d5e; // 水柱外壳背景色
position: relative;
border-radius: 50px;
overflow: hidden;
}
overflow: hidden
确保内部液面圆帽不溢出容器边界- 使用
::before
和::after
构建顶部与底部的圆帽
2、 .lui-inner
cpp
.lui-inner {
position: absolute;
bottom: 0;
transition: height 0.5s ease-in-out;
}
bottom: 0
让水面从底部"涨"起来transition
让数值变化动画流畅过渡
3、.lui-inner::before / ::after
-
::before
是水柱顶部的圆形高光 -
::after
是底部的白色圆边,用来模拟"液面"
4、.lui-inner-text
cpp
.lui-inner-text {
color: white;
font-size: 14px;
font-weight: bold;
position: relative;
z-index: 2;
}
显示中间百分比文字,保持在水柱内层上方,不被遮挡
五、可配置参数说明
参数 | 类型 | 说明 |
---|---|---|
num | Number | 当前数值 |
target | Number | 目标数值 |
width | Number | 圆柱宽度(单位:px) |
height | Number | 圆柱高度 |
innerColor | String | 水柱颜色 |
topColor | String | 圆柱顶部颜色(备用) |
bottomColor | String | 圆柱底部颜色(备用) |
showPercent | Boolean | 是否显示百分比文字 |
完整代码
cpp
<template>
<div
class="lui-column-bg"
:style="{ width: width + 'px', height: height + 'px' }"
>
<div
class="lui-inner"
:style="{
height: validPercentage + '%',
backgroundImage: `linear-gradient(to top, ${innerColor}, ${innerColor})`
}"
>
<div class="lui-inner-text" v-if="showPercent">
{{ validPercentage }}%
</div>
</div>
</div>
</template>
<script>
export default {
name: 'LiquidColumn',
props: {
num: { type: Number, default: 0 },
target: { type: Number, default: 0 },
width: { type: Number, default: 100 },
height: { type: Number, default: 190 },
innerColor: { type: String, default: '#28b0bd' },
topColor: { type: String, default: '#54d8de' },
bottomColor: { type: String, default: '#54d8de' },
showPercent: { type: Boolean, default: true },
},
computed: {
validPercentage() {
if (this.target <= 0) return 0;
const percent = ((this.num / this.target) * 100).toFixed(2);
return Math.min(Math.round(percent), 100); // 取整,防止超过 100%
},
},
};
</script>
<style lang="scss" scoped>
.lui-column-bg {
position: relative;
width: 100px;
height: 190px;
margin: 30px auto;
background-color: transparent;
background-color: #2a4d5e;
}
.lui-column-bg:before {
position: absolute;
content: "";
display: block;
height: 20px;
width: 100%;
border-radius: 50%;
top: -10.5px;
z-index: 1;
background-color: rgb(101 221 197);
}
.lui-column-bg:after {
position: absolute;
content: "";
display: block;
height: 15px;
width: 100%;
border-radius: 50%;
bottom: -10px;
background-color: #54d8de;
}
.lui-inner {
position: absolute;
bottom: 0;
width: 100%;
height: 50%;
background-image: linear-gradient(to top, #28b0bd, #28b0bd);
text-align: center;
}
.lui-inner::before {
position: absolute;
content: "";
display: block;
height: 20px;
width: 100%;
background-color: #54d8de;
border-radius: 50%;
top: -10.5px;
z-index: 1;
}
.lui-inner:after {
position: absolute;
content: "";
display: block;
height: 15px;
width: 100%;
border-radius: 50%;
background-color: white;
bottom: -10px;
}
.lui-inner-text {
color: white;
position: absolute;
top: 10px;
width: 100%;
font-size: 18px;
font-weight: bold;
}
</style>
使用实例
1、引入组件
cpp
import LiquidColumn from "../components/LiquidColumn";
2、注册组件
cpp
<script>
export default {
components: { LiquidColumn },
}
</script>
3、调用组件
cpp
<!-- 具体参数自行添加 -->
<liquid-column :num="50" :target="100" :showPercent="false"/>
实现效果
总结
组件相对简单,还可以从水面波动动画、颜色渐变、点击事件等方面去优化。