如何实现支持API返回动态不等距刻度的SVG半圆仪表盘
非等距动态刻度SVG仪表盘实现方案
需求说明
我正在尝试制作如下效果的SVG仪表盘:
- 刻度完全动态,由API返回
- 刻度放置在接口返回的百分比数值数组对应的位置,无需有序
- 两个刻度之间的间距无需相等
初始实现尝试
初始版本使用半圆加stroke-dasharray实现,但原逻辑只支持等距刻度,不符合非等距需求:
初始JS代码
let divisionsElement = document.getElementById('divisions'); let length = parseInt(divisionsElement.getAttribute("r")) * Math.PI; let divisionsArray = [20,30,80,90]; //stroke at 20%, 30%,80%,90% position let METER_DIVISIONS_GAP = 0.2; divisionsArray.map(ele => { setupPath(ele); }) function setupPath(val) { divisionsElement.setAttribute("stroke-dasharray", val + ' ' +METER_DIVISIONS_GAP); }
初始CSS代码
circle { fill: none; } .circle-back { stroke: #F8F6F0; stroke-width:9px; } .circle-fill { stroke: grey; stroke-width: 10px; } .circle-progress { stroke: blue; stroke-width:9px; }
初始HTML代码
<svg viewBox="0 0 126 126" preserveAspectRatio="xMinYMin meet"> <clipPath id="cut-off"> <rect x="0" y="0" width="100%" height="50" /> </clipPath> <clipPath id="progress-percent"> <path x="0" y="0" width="12%" height="50" /> </clipPath> <g> <circle r="35%" cx="40%" cy="40%" class="circle-fill" clip-path="url(#cut-off)" /> <circle id="divisions" r="35%" cx="40%" cy="40%" class="circle-back" clip-path="url(#cut-off)" stroke-dasharray="20 0.5" stroke-dashoffset="25" /> <circle r="35%" cx="40%" cy="40%" class="circle-progress" clip-path="url(#progress-percent)" /> </g> </svg>
IE兼容优化方案
如果需要兼容IE,可以使用以下基于path路径的实现方案:
兼容版JS代码
var data = [10,20,10,10,25,25]; var dataSum = 0; for (let i = 0; i < data.length - 1; i++) { dataSum += data[i]; } var drawArray = ""; var indexOfFill = 2; var arcPath = document.getElementById("meter-back"); var filledPath = document.getElementById("meter-fill"); var totalLength = arcPath.getTotalLength(); function drawPath() { // Draw the background strokes let test=''; for (i = 0; i < data.length; i++) { let barLen = ((data[i]) * totalLength) / 100; test = test + ((barLen) - (0.2 * (dataSum / 100)) + " " + 0.2 * (dataSum / 100) + " ") } arcPath.setAttribute("stroke-dasharray", test); animate(filledPath, totalLength, 0,50); }drawPath(); function animate(el,length,end,percentage) { el.style.visibility = 'visible'; if (end <= percentage) { el.style.strokeOpacity = 5; let gap = 100 - end; let offset = 100; el.style.strokeDasharray = ((end / 100) * length) + ' ' + ((gap / 100) * length); el.style.strokeDashoffset = (offset / 100) * length; animate(el, length, ++end, percentage); } }
兼容版HTML代码
<svg width="100%" height="600px" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet"> <defs> <filter id="outline" y="-50%" height="200%"> <feMorphology operator="erode" radius="0.2" /> <feComposite operator="out" in="SourceGraphic" result="outline" /> <feFlood flood-color="grey" /> <feComposite operator="in" in2="outline" /> <feComposite operator="atop" in2="SourceGraphic" /> </filter> </defs> <g filter="url(#outline)"> <path fill="none" stroke-dasharray="" stroke-width="20" stroke="grey" d="M30 100 A 40 40 0 0 1 170 100" /> <path id="meter-back" fill="none" stroke-dasharray="" stroke-width="20" stroke="white" d="M30 100 A 40 40 0 0 1 170 100" /> <path id="meter-fill" fill="none" stroke-dasharray="" stroke="rgba(128,128,128,0.5)" stroke-width="20" d="M30 100 A 40 40 0 0 1 170 100" style="visibility: hidden;" ></path> </g> </svg>
内容的提问来源于stack exchange,提问作者Kshri
相关产品推荐
相关产品推荐

