【问题标题】:SVG gauge meter with dynamic strokes具有动态行程的 SVG 仪表
【发布时间】:2021-11-11 04:46:24
【问题描述】:

我正在尝试构建类似于以下内容的 SVG:

笔画是完全动态的,因为它们来自 API。我想将笔画放在收到的点上(作为百分比值数组)。不必整齐,两笔之间的距离不必相等

我正在尝试类似下面的方法,但无法提出笔画放置的逻辑。我试图在这里遵循答案:https://stackoverflow.com/a/66076805/6456247 但这里的笔画之间的距离是相等的。在我的场景中,它们并不一致。

小提琴链接:https://jsfiddle.net/puq8v594/2/

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);
}
circle {
  fill: none;
}
.circle-back {
  stroke: #F8F6F0;
  stroke-width:9px;
}

.circle-fill {
  stroke: grey;
  stroke-width: 10px;
}

.circle-progress {
  stroke: blue;
  stroke-width:9px;
}
<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 的情况下实现此功能的人,我可以通过对 Michael 的代码进行一些更改来实现它。

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);
        }

}  
<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>

【问题讨论】:

    标签: javascript html css svg meter


    【解决方案1】:

    将 pathLength 设置为 100(或接近 100)的弧形路径可能更容易做到这一点。

    let data =[5 , 10 , 25  ,25 , 6 , 9  , 20];
    let i=0;
    let drawArray = "";
    let arcPath = document.getElementById("meter");
    
    function setupPath() {
        for (i=0; i < data.length; i++) {
          drawArray = drawArray + (data[i] - 0.5) + " 0.5 ";
        }
      arcPath.setAttribute("stroke-dasharray", drawArray);
    }; setupPath();
    <svg width="800px" height="600px" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet">
     
        <path id="meter" fill="none" stroke-dasharray="" stroke-width="20" stroke="blue" pathLength="99.5" d="M30 100 A 40 40 0 0 1 170 100" />
      
    </svg>

    更新:

    我从这个问题中想到,您期望一组百分比值加起来为 100 作为输入。但是,如果您不知道值的数量或它们的总和,那么您可以按如下方式对数据进行归一化。此外,您可以使用过滤器添加轮廓 - 这样您就不必进行大量数学运算来显式绘制弧线段。

    我还意识到您想要部分填充 - 因此您可以通过更改变量 indexOfFill 来控制仪表的填充位置。

    var data = [5, 10 ,25 ,25 ,6 ,9 ,20 ,16 ,33 ,45 ,67];
    var dataSum = data.reduce((partial_sum, a) => partial_sum + a, 0);
    var indexOfFill = 3;
    
    var i=0;
    var drawArray = "";
    var arcPath = document.getElementById("meter-back");
    var filledPath = document.getElementById("meter-fill");
    
    
    function setupPath() {
      
      // Draw the background
        for (i=0; i < data.length; i++) {
          drawArray = drawArray + (data[i] - (0.5 * (dataSum/100)) + " " + 0.5*(dataSum/100) +" ")};
        arcPath.setAttribute("stroke-dasharray", drawArray);
        arcPath.setAttribute("pathLength", dataSum - (0.5 * (dataSum/100)));
      
      //Draw the fill
      drawArray="";
        for (i=0; i < indexOfFill; i++) {
          if ( (i + 1) === indexOfFill ) {
            drawArray = drawArray + (data[i] - (0.5 * (dataSum/100)) + " " + dataSum)
          } else {
            drawArray = drawArray + (data[i] - (0.5 * (dataSum/100)) + " " + 0.5*(dataSum/100) +" ")}};
        filledPath.setAttribute("stroke-dasharray", drawArray);
        filledPath.setAttribute("pathLength", dataSum - (0.5 * (dataSum/100)));  
      
    }; setupPath();
    <svg width="800px" 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="black"/>
        <feComposite operator="in" in2="outline"/>
        <feComposite operator="atop" in2="SourceGraphic"/>
       </filter>
      </defs>
      
      <g filter="url(#outline)">
        <path id="meter-back" fill="none" stroke-dasharray="" stroke-width="20" stroke="white" pathLength="99.5" d="M30 100 A 40 40 0 0 1 170 100" />
        
        <path id="meter-fill" fill="none" stroke-dasharray="" stroke-width="20" stroke="rgb(90,90,220)" pathLength="99.5" d="M30 100 A 40 40 0 0 1 170 100" />
      </g>
    </svg>

    【讨论】:

    • 非常感谢!但是使用这种方法如何管理绘制外边框?
    • 另外,我尝试了不同的数据集:let data =[40,60,80, 20] 似乎正在崩溃
    • 更新了更完整的解决方案
    • 嗨迈克尔,知道为什么这在 IE11 上不起作用吗?除了 reduce 函数和箭头语法。我确定该浏览器不支持某些内容。但无法找出是什么,所以我可以找到替代方案。
    • IE11 中从不支持 pathLength,getTotalLength() 也不支持,因此您必须通过手动计算弧的长度来标准化 stroke-dash 数组 - 这很容易,因为它只是一个半圆 - 只是 Math.Pi * r -> 在我的例子中 r 是 40。但是现在 IE11 几乎没有市场份额——我个人不会费心去支持它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 2021-03-14
    • 2019-12-12
    • 2017-04-04
    • 2018-03-24
    相关资源
    最近更新 更多