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>好了,本章節的內容就到這里,如小伙伴有疑問,可評論區留言、隨時交流。
總結
以上是生活随笔為你收集整理的CSS锥形渐变实现环形进度条的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ansible中的变量
- 下一篇: CSS 基本样式