100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > CSS锥形渐变实现环形进度条

CSS锥形渐变实现环形进度条

时间:2023-12-07 09:33:40

相关推荐

CSS锥形渐变实现环形进度条

10月份因为疫情原因、又开启了居家办公模式,空闲之余,与其选择“躺平”,不如去做一些有意义的事情,内心的想法驱使着我去做些什么,但是又没有合适的素材,直到接手了最近的一个可视化项目,一个图表勾起了我无限的好奇心,本着对技术死磕到底的想法,于是开启了我的探索之旅。具体的原型效果如下:

关于此类进度条的实现方式,在我之前的章节(SVG绘制圆环进度条)中也有涉及,本章则另辟蹊跷,从另一个维度简单介绍一下CSS锥形渐变(conic-gradient)在可视化图表中的应用场景。本章依旧采用vue+原生css的形式进行案例展示、在了解本章节之前,需要对vue框架、css变量、css属性conic-gradient有一定程度的认识。案例实现效果如下:

实现思路:首先从原型图入手,我们可以将效果图进行拆分,背景圆环+进度圆环+进度条开始处小圆点(和边框一样大小、模拟圆角效果)+进度尾部圆点+进度尾部小眼睛+进度条中心内容。因此我们只需要将以上几个小功能点实现即可

1.背景圆环:div添加背景颜色+圆角

2.进度圆环:使用css属性conic-gradient进行进度控制

3.进度条开始处小圆点:使用伪元素(::before或::after)或div均可,定位解决

4.进度条尾部圆点:相当于在一个指针上添加一个小球,然后将指针根据数值旋转一定的角度

5.进度尾部小眼睛:使用指针头部小球元素的伪元素进行定位

6.进度条中心内容:可根据需要,使用插槽的形式解决

首先看一下前两个图表的具体实现细节:

<!-- demo1.vue --><template><div class="chart-box" :style="styObj"><!-- 进度条部分 --><div class="outer-box"><div class="inner-box"><div class="pointer-box"></div></div></div><!-- 插槽内容 --><div class="slot-content"><slot></slot></div></div></template><script>export default {props: {rate: {type: Number,default: 0,},config: {type: Object,default: () => {return {};},},},computed: {styObj() {let rate = 0;if (this.rate <= 0) {rate = 0;} else if (this.rate >= 1) {rate = 1;} else {rate = this.rate;}let endPos = `${rate * 100}%`;let obj = Object.assign({}, this.defaultConfig, this.config);let rotate = `rotate(${360 * rate}deg)`;let chartRotate = obj.clockwise ? "rotateY(0deg)" : "rotateY(180deg)";let showEyes = obj.showEyes ? 1 : 0;return {"--background-image": `conic-gradient(${obj.startColor} 0%, ${obj.endColor} ${endPos}, transparent ${endPos})`,"--border-width": obj.borderWidth,"--dot-width": obj.circleSize,"--pointer-rotate": rotate,"--background-color": obj.borderBackground,"--center-gap-bg": obj.centerCircleBg,"--circle-color": obj.circleColor,"--clockwise-wise": chartRotate,"--show-eyes": showEyes,"--eyes-size": obj.eyesSize,"--start-color": obj.startColor,};},},data() {return {/* 此配置下所有属性均可在config中进行覆盖,实现个性化配置 */defaultConfig: {borderWidth: "8px", // 描边宽度borderBackground: "#eee", // 描边背景颜色circleSize: "16px", // 结尾处圆点直径circleColor: "#2ec4a7", // 结尾处圆点颜色startColor: "#d5f4ee", // 进度条起始颜色endColor: "#2ec4a7", // 进度条结束颜色centerCircleBg: "#fff", // 中间空心圆背景clockwise: true, // 是否顺时针showEyes: false, // 是否显示结尾处小眼睛eyesSize: "8px", // 结尾处小眼睛大小},};},};</script><style scoped>.chart-box {position: relative;width: 100%;height: 100%;}/* 核心代码、控制进度条样式及进度 */.outer-box {width: 100%;height: 100%;border-radius: 50%;box-sizing: border-box;background-color: var(--background-color);background-image: var(--background-image);padding: var(--border-width);transform: var(--clockwise-wise);}/* 开始处增加一个圆形端点, 模拟圆角效果 */.outer-box::after {content: "";width: var(--border-width);height: var(--border-width);border-radius: 50%;position: absolute;left: 50%;top: 0;transform: translateX(-50%);background: var(--start-color);}/* 中间添加一个和背景色一样的圆圈 */.inner-box {position: relative;width: 100%;height: 100%;border-radius: 50%;background: var(--center-gap-bg);}/* 指示针 */.pointer-box {position: absolute;left: 50%;top: calc(0px - var(--border-width) / 2);bottom: calc(0px - var(--border-width) / 2);z-index: 1;transform-origin: center center;transform: var(--pointer-rotate);}/* 指示针的头部添加一个小圆点 */.pointer-box::after {content: "";position: absolute;left: 50%;top: 0;width: var(--dot-width);height: var(--dot-width);border-radius: 50%;background: var(--circle-color);transform: translate(-50%, -50%);}/* 进度条结尾处添加一个小眼睛,背景白色 */.pointer-box::before {content: "";position: absolute;left: 50%;top: 0;width: var(--eyes-size);height: var(--eyes-size);border-radius: 50%;background: #fff;transform: translate(-50%, -50%);z-index: 1;opacity: var(--show-eyes);}/* 插槽内容样式 */.slot-content {position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;}</style>

分析:从代码中不难看出、进度圆环中间的空白地方(class类名为“inner-box”)使用了一个背景为白色的元素进行遮盖,这点需要根据具体场景进行微调,在纯色背景下并无大碍,但是在有背景图的场景下,显示效果就有点差强人意了。因此此处需要做一下优化,扒一扒css手册,刚好有一个属性可以解决这个问题,那就是mask属性了,优化后代码如下,实现效果见第三、四个图表

<!-- demo2 --><template><div class="chart-box" :style="styObj"><!-- 进度条部分 --><div class="process-wrapper"><div class="process-box"></div><div class="pointer-box"></div></div><!-- 插槽内容 --><div class="slot-content"><slot></slot></div></div></template><script>export default {props: {rate: {type: Number,default: 0,},config: {type: Object,default: () => {return {};},},},computed: {styObj() {let rate = 0;if (this.rate <= 0) {rate = 0;} else if (this.rate >= 1) {rate = 1;} else {rate = this.rate;}let endPos = `${rate * 100}%`;let obj = Object.assign({}, this.defaultConfig, this.config);let rotate = `rotate(${360 * rate}deg)`;let chartRotate = obj.clockwise ? "rotateY(0deg)" : "rotateY(180deg)";let showEyes = obj.showEyes ? 1 : 0;return {"--background-image": `conic-gradient(${obj.startColor} 0%, ${obj.endColor} ${endPos}, transparent ${endPos})`,"--border-width": obj.borderWidth,"--dot-width": obj.circleSize,"--pointer-rotate": rotate,"--background-color": obj.borderBackground,"--circle-color": obj.circleColor,"--clockwise-wise": chartRotate,"--show-eyes": showEyes,"--eyes-size": obj.eyesSize,"--start-color": obj.startColor,};},},data() {return {/* 此配置下所有属性均可在config中进行覆盖,实现个性化配置 */defaultConfig: {borderWidth: "8px", // 描边宽度borderBackground: "#eee", // 描边背景颜色circleSize: "16px", // 结尾处圆点直径circleColor: "#2ec4a7", // 结尾处圆点颜色startColor: "#d5f4ee", // 进度条起始颜色endColor: "#2ec4a7", // 进度条结束颜色clockwise: true, // 是否顺时针showEyes: false, // 是否显示结尾处小眼睛eyesSize: "8px", // 结尾处小眼睛大小},};},};</script><style scoped>.chart-box {position: relative;width: 100%;height: 100%;}/* 将图表和插槽内容分开,便于控制进度条顺时针亦或是逆时针 */.process-wrapper {position: relative;width: 100%;height: 100%;transform: var(--clockwise-wise);}/* 开始处增加一个圆形端点,模拟圆角效果 */.process-wrapper::after {content: "";width: var(--border-width);height: var(--border-width);border-radius: 50%;position: absolute;left: 50%;top: 0;transform: translateX(-50%);background: var(--start-color);}/* 核心代码、控制进度条样式及进度 */.process-box {position: absolute;left: 0;top: 0;width: 100%;height: 100%;border-radius: 50%;box-sizing: border-box;background-color: var(--background-color);background-image: var(--background-image);-webkit-mask: radial-gradient(closest-side at center center,transparent calc(100% - var(--border-width)),#fff calc(100% - var(--border-width)));mask: radial-gradient(closest-side at center center,transparent calc(100% - var(--border-width)),#fff calc(100% - var(--border-width)));}/* 指示针 */.pointer-box {position: absolute;left: 50%;top: calc(0px + var(--border-width) / 2);bottom: calc(0px + var(--border-width) / 2);z-index: 1;transform: var(--pointer-rotate);}/* 指示针的头部(进度条结尾处)添加一个小圆点 */.pointer-box::after {content: "";position: absolute;left: 50%;top: 0;width: var(--dot-width);height: var(--dot-width);border-radius: 50%;background: var(--circle-color);transform: translate(-50%, -50%);}/* 进度条结尾处添加一个小眼睛,背景白色 */.pointer-box::before {content: "";position: absolute;left: 50%;top: 0;width: var(--eyes-size);height: var(--eyes-size);border-radius: 50%;background: #fff;transform: translate(-50%, -50%);z-index: 1;opacity: var(--show-eyes);}/* 插槽内容样式 */.slot-content {position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;}</style>

至此、已完成了进度条的优化改造、不过还存在一个小小的瑕疵、使用mask后,进度条内部交合区域稍微也有一点锯齿感,这个暂时还没有找到优化措施,不过并无大碍。

为了实现更加丰富的展现形式、我们可以在进度条上添加分割线实现花纹效果、这个其实也不麻烦、只需要再添加一层锥形渐变即可解决,具体实现如下:

<!-- demo3 --><template><div class="chart-box" :style="styObj"><div class="process-box"><div class="center-mask"></div></div><!-- 插槽内容 --><div class="slot-content"><slot></slot></div></div></template><script>export default {props: {rate: {type: Number,default: 0,},config: {type: Object,default: () => {return {};},},},computed: {styObj() {let rate = 0;if (this.rate <= 0) {rate = 0;} else if (this.rate >= 1) {rate = 1;} else {rate = this.rate;}let endPos = `${rate * 100}%`;let obj = Object.assign({}, this.defaultConfig, this.config);let rotate = `rotate(${360 * rate}deg)`;let chartRotate = obj.clockwise ? "rotateY(0deg)" : "rotateY(180deg)";let bgInfo = [];let gap = 100 / obj.gapNum;for (let i = 0; i < obj.gapNum; i++) {bgInfo.push(`#fff ${i * gap}%`);bgInfo.push(`#fff ${i * gap + obj.lineWidth}%`);bgInfo.push(`transparent ${i * gap + obj.lineWidth}%`);bgInfo.push(`transparent ${(i + 1) * gap}%`);}return {"--background-image": `conic-gradient(${bgInfo.join(",")})`,"--background-image1": `conic-gradient(${obj.startColor} 0%, ${obj.endColor} ${endPos}, transparent ${endPos})`,"--border-width": obj.borderWidth,"--background-color": obj.borderBackground,"--center-gap-bg": obj.centerCircleBg,"--clockwise-wise": chartRotate,};},},data() {return {/* 此配置下所有属性均可在config中进行覆盖,实现个性化配置 */defaultConfig: {borderWidth: "8px", // 描边宽度borderBackground: "#eee", // 描边背景颜色startColor: "#d5f4ee", // 进度条起始颜色endColor: "#2ec4a7", // 进度条结束颜色centerCircleBg: "#fff", // 中间空心圆背景clockwise: true, // 是否顺时针gapNum: 10, // 分割段数lineWidth: 2, // 间隔线宽度,百分比},};},};</script><style scoped>.chart-box {position: relative;width: 100%;height: 100%;}.process-box {position: relative;width: 100%;height: 100%;border-radius: 50%;padding: var(--border-width);box-sizing: border-box;background-color: var(--background-color);background-image: var(--background-image), var(--background-image1);transform: var(--clockwise-wise);}.center-mask {width: 100%;height: 100%;border-radius: 50%;background: var(--center-gap-bg);}/* 插槽内容样式 */.slot-content {position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;}</style>

最后,在汇总页面中依次将三个组件引入,增加不同的个性化参数,即可实现封面的展示效果,贴一下汇总页面代码

<template><div class="page-box"><div class="main-box"><!-- 第一种实现方式,中间的镂空部分采用背景色(和页面背景一致)的形式 --><div class="module"><conic-gradient :rate="0.8888"><span class="slot-font1">88.88%</span></conic-gradient></div><div class="module"><conic-gradient :rate="0.8888" :config="config"><div class="slot-bg"><span class="slot-font2">88.88%</span></div></conic-gradient></div><!-- 第二种实现方式,中间镂空部分采用遮罩(mask)的方式实现 --><div class="module"><conic-mask:rate="0.6666":config="{ showEyes: true, eyesSize: '6px', circleSize: '8px' }"><span class="slot-font1">66.66%</span></conic-mask></div><div class="module"><conic-mask :rate="0.6666" :config="config"><div class="slot-bg"><span class="slot-font2">66.66%</span></div></conic-mask></div></div><hr /><!-- 锥形渐变实现花纹进度条 --><div class="main-box"><div class="module"><conic-process :rate="0.6666"><span class="slot-font1">66.66%</span></conic-process></div><div class="module"><conic-process:rate="0.8888":config="{startColor: '#e45739',endColor: '#e45739',borderBackground: '#fbedea',gapNum: 20,lineWidth: 1,clockwise: false}"><div class="slot-bg"><span class="slot-font2">88.88%</span></div></conic-process></div><div class="module"><conic-process :rate="0.8888" :config="{ gapNum: 1, lineWidth: 0 }"><span class="slot-font1">88.88%</span></conic-process></div><div class="module"><conic-process:rate="0.8888":config="{startColor: '#e45739',endColor: '#e45739',borderBackground: '#fbedea',gapNum: 1,lineWidth: 0,clockwise: false,}"><div class="slot-bg"><span class="slot-font2">88.88%</span></div></conic-process></div></div></div></template><script>import ConicGradient from "./demo1";import ConicMask from "./demo2";import ConicProcess from "./demo3";export default {components: {ConicGradient,ConicMask,ConicProcess,},data() {return {config: {borderWidth: "8px",circleSize: "16px",circleColor: "#e45739",borderColor: "#d5f4ee",startColor: "#eead99",endColor: "#e45739",borderBackground: "#fbedea",centerCircleBg: "#fff",clockwise: false,showEyes: true,},};},};</script><style scoped>.page-box {width: 100%;height: 100%;overflow: auto;}.main-box {display: flex;flex-wrap: wrap;width: 100%;}.module {width: 200px;height: 200px;box-sizing: border-box;padding: 20px;}.slot-bg {display: flex;align-items: center;justify-content: center;width: 75%;height: 75%;border-radius: 50%;background: #fbedea;}.slot-font1 {color: #009d84;font-size: 20px;font-weight: bold;}.slot-font2 {color: #e45638;font-size: 20px;font-weight: bold;}</style>

好了,本章节的内容就到这里,如小伙伴有疑问,可评论区留言、随时交流。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。