本篇文章记录仿写一个
el-progress
组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续空闲了会不断更新并仿写其他组件。源码在github上,大家可以拉下来,npm start运行跑起来,结合注释有助于更好的理解。GitHub仓库地址:https://github.com/shuirongsh...
组件分析
组件效果图
我们先看下组件效果图:
这里笔者没有考虑垂直方向的进度条,读者们将代码拷贝下来进行改造哦,动动手多敲敲
组件介绍
进度条组件一般分为三种,一种是横向进度条(最为常用)、另两种是圆形进度条和仪表盘形进度条,本文说一下横向进度条的实现方式,值得一提的是无论是饿了么还是iview还是antd是吧三种进度条融合在一个组件中去的。
为了便于大家更好理解,我这里把进度条进行更为细致的拆分:
- my-progress1 横向进度条
- my-progress2 圆形进度条&仪表盘进度条
本文单说横向进度条(my-progress1),后文会说另外两种进度条
备注:若大家感觉仪表盘进度条不太适合,也可使用echarts中的仪表盘,总之实现方案有多种
组件结构
在封装一个组件之前,我们首先要考虑组件的dom结构,然后才是组件接收的props参数,一个横向进度条的dom结构分为:
- 进度条容器
- 进度条
- 文字内容区
- 最后再用一个大盒子包着形成一个整体即可
如下图:
对应html结构就是:
<!-- 4.大盒子包起来 -->
<div class="myProgressWrap">
<!-- 1.进度条容器 -->
<div class="progressBar">
<!-- 2.进度条(背景色的变换) -->
<div :class="['progressBarBg', status]" :style="barStyle"></div>
</div>
<!-- 3.文字内容区(要考虑自定义内容使用插槽) -->
<div class="progressText" v-if="showText">
<span v-if="!$slots.text">{{ percentage }}%</span>
<slot v-else name="text"></slot>
</div>
</div>
组件的功能
- 预设几种默认样式进度条颜色
status参数
- 百分比从0到100
percentage参数
- 可自定义设置进度条的颜色
字符串color参数
- 可让进度条在不同数值下显示不同颜色
函数color参数
新知识点css中通过var函数使用js中的变量
关于进度条组件没有太多新的知识点,都是笔者曾经封装组件提到过的,不过这里有一个新的知识点
什么是css中的var函数
关于css中的var函数,这里不赘述,忘记知识点的同学,可以看下官方文档:
https://developer.mozilla.org...
这里简述一下使用var函数的步骤
第一步 html标签中使用:style绑定
<div :class="['progressBarBg', status]" :style="
{
width: "60%",
"--displayVal": displayVal, // 注意,这里的key需要使用--xxx开头,value则是data中的数据
}
"></div>
第二步 在data或者props或者computed中要定义的有值
data() {
return {
displayVal: "none", // css中使用js中变量的具体值
};
},
第三步 css中使用var函数读取使用
.progressBarBg::before {
display: var(--displayVal);
// 相当于:display: none
content: "";
...
}
- 为什么需要提到要在css中使用js中的变量数据呢?因为进度条有一个动画效果,当外界传进来showDongHua变量为true时,需要展示这个动画,为false时就不要这个动画。
- 这里就是js中的一个变量去控制css中的具体值
- 换句话说,就是css通过使用js中的变量数据,去操作动画是否显示
- 即:css中使用js中的变量
代码详情
这里大家复制粘贴,结合注释进行调试,就能够理解了😄
笔者就不啰嗦啦😉
使用时的代码
<template>
<div>
<h3>进度条</h3>
<br>
<h5>percentage百分比,status三种自带颜色,默认normal</h5>
<h4><my-progress1 :percentage="30"></my-progress1></h4>
<h4><my-progress1 :percentage="40" status="normal"></my-progress1></h4>
<h4><my-progress1 :percentage="50" status="success"></my-progress1></h4>
<h4><my-progress1 :percentage="60" status="fail"></my-progress1></h4>
<br>
<h5>自定义颜色(字符串)</h5>
<h4><my-progress1 color="pink" :percentage="70"></my-progress1></h4>
<br>
<h5>自定义颜色(函数,可根据百分比设置不同的颜色)</h5>
<h4><my-progress1 :color="colorFn" :percentage="60"></my-progress1></h4>
<h4><my-progress1 :color="colorFn" :percentage="50"></my-progress1></h4>
<br>
<h5>进度条右侧文本区域插槽</h5>
<h4>
<my-progress1 :percentage="40">
<span slot="text">₍ᐢ..ᐢ₎</span>
</my-progress1>
</h4>
<h4>
<my-progress1 :percentage="100">
<span slot="text">满</span>
</my-progress1>
</h4>
<br />
<h5>隐藏右侧文本区域插槽</h5>
<h4>
<my-progress1 :percentage="100" :showText="false"></my-progress1>
</h4>
<br>
<h5>百分比增加减少,需要控制一下增加和减少的边界值0和100</h5>
<h4>
<my-progress1 :percentage="percentage" :color="diffColor"></my-progress1>
<button @click="add">增加</button>
<button @click="subtra">减少</button>
</h4>
<br />
<h5>开启动画,默认动画关闭的</h5>
<h4><my-progress1 :percentage="70" showDongHua></my-progress1></h4>
</div>
</template>
<script>
export default {
name: "myProgress1Name",
data() {
return {
percentage: 15,
};
},
methods: {
colorFn(val) {
// console.log("不同百分比区间显示不同颜色", val);
if (val <= 50) {
return "green";
}
if (val <= 100) {
return "purple";
}
},
add() {
if (this.percentage + 10 >= 100) {
this.percentage = 100;
} else {
this.percentage = this.percentage + 10;
}
},
subtra() {
if (this.percentage - 10 <= 0) {
this.percentage = 0;
} else {
this.percentage = this.percentage - 10;
}
},
diffColor(val) {
if (val <= 10) {
return "red";
}
if (val <= 20) {
return "orange";
}
if (val <= 30) {
return "yellow";
}
if (val <= 40) {
return "green";
}
if (val <= 50) {
return "cyan";
}
if (val <= 60) {
return "blue";
}
if (val <= 70) {
return "purple";
}
if (val <= 80) {
return "pink";
}
if (val <= 90) {
return "#baf";
}
if (val <= 100) {
return "#F24E1E";
}
},
},
};
</script>
封装组件的代码
<template>
<div class="myProgressWrap">
<div class="progressBar">
<div :class="['progressBarBg', status]" :style="barStyle"></div>
</div>
<div class="progressText" v-if="showText">
<!-- 没传递text插槽就显示百分比,反之显示插槽 -->
<span v-if="!$slots.text">{{ percentage }}%</span>
<slot v-else name="text"></slot>
</div>
</div>
</template>
<script>
// 自带三种状态颜色,默认normal
const statusArr = ["success", "fail", "normal"];
export default {
name: "myProgress1",
props: {
percentage: {
type: Number,
default: 0,
validator: (val) => {
return (val >= 0) & (val <= 100);
},
},
status: {
type: String,
validator: (val) => {
return statusArr.includes(val);
},
},
// 进度条颜色设置,字符串或函数
color: {
type: [String, Function],
},
// 默认展示文本区域内容
showText: {
type: Boolean,
default: true,
},
// 是否开启动画
showDongHua: {
type: Boolean,
default: false,
},
},
watch: {
showDongHua: {
handler(newVal) {
if (newVal) {
this.displayVal = "";
} else {
this.displayVal = "none"; // 障眼法,隐藏伪元素,就没动画了
}
},
immediate: true,
},
},
data() {
return {
displayVal: "", // css中使用js中变量的具体值
};
},
computed: {
barStyle() {
// style是对象,故返回一个对象
return {
width: this.percentage + "%",
backgroundColor: this.setBg(), // 计算得出背景色
/**
* css中使用var函数步骤:
* 1. :style中使用--xxx绑定一个js中数据的值
* 2. js中要定义这个值便于能找到
* 3. css中key: value键值对中 value使用var函数替换一下var(--xxx)
* */
"--displayVal": this.displayVal,
};
},
},
methods: {
setBg() {
if (typeof this.color === "string") {
return this.color;
}
if (typeof this.color === "function") {
// 可根据不同百分比计算出对应背景色
return this.color(this.percentage);
}
},
},
};
</script>
<style ref='xxx' lang='less' scoped>
.myProgressWrap {
width: 100%;
display: flex;
align-items: center;
.progressBar {
flex: 1;
height: 6px;
background-color: #f3f3f3;
border-radius: 6px;
overflow-x: hidden;
.progressBarBg {
height: 100%;
border-radius: 6px;
// 默认normal
background-color: #1677ff;
// 加一个过渡,看着宽度变化自然些
transition: width 0.36s ease;
position: relative;
}
.progressBarBg::before {
display: var(--displayVal);
content: "";
opacity: 0;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 10px;
animation: donghua 2s infinite;
}
.success {
background-color: #52c41a;
}
.fail {
background-color: #ff4d4f;
}
}
.progressText {
margin-left: 6px;
width: fit-content; // 宽度随内容区自适应
}
}
@keyframes donghua {
0% {
opacity: 0.5;
width: 0;
}
100% {
opacity: 0;
width: 100%;
}
}
</style>
总结
A bad pen is better than a good memory
- 完整代码在github上哦,也可以直接访问网址看效果(还有其他笔者封装的组件)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。