将箭头附加到 SVG 弧形路径

问题描述 投票:0回答:1

我正在使用 svg 创建一个仪表。

有没有办法在动画仪表的末尾添加一个箭头来指示输入的值?

由于仪表是用路径的行程-破折号数组显示的,因此似乎无法根据 svg 规范应用标记。

class arcGauge {
    constructor(targetEl) {
        this.el = targetEl;
        this.minmax = [];
        this.arcCoordinate = [];
        this.valueArcDataValue = 0;
        this.gaugeArc = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    }

    // draw gauge
    init(data) {
        // set data
        this.viewBox = [0, 0, 110, 100];
        this.minmax = data.minmax || [];
        this.arcCoordinate = data.arcCoordinate || [120, 60];
        this.threshold = data.threshold || [];
        this.valueArcData = data.valueArcData;
        this.gaugeRadius = 42;
        this.gaugeStrokeWidth = 6;

        this._makeGauge();

    }

    _makeGauge() {
        const radius = this.gaugeRadius;

        let arcCoord = [];
        // coordinate
        this.arcCoordinate.forEach((ang) => {
            const radian = this._degreesToRadians(ang);
            const x = this.viewBox[2] / 2 + Math.cos(radian) * radius;
            const y = this.viewBox[2] / 2 + Math.sin(radian) * radius;
            arcCoord.push([x.toFixed(2), y.toFixed(2)]);
        });

        //arc
        this.gaugeArc.setAttribute('id', 'gaugeArc');
        this.gaugeArc.setAttribute('d', `M ${arcCoord[0][0]} ${arcCoord[0][1]} A ${radius} ${radius} 0 1 1  ${arcCoord[1][0]} ${arcCoord[1][1]}`);
        this.gaugeArc.setAttribute('fill', 'none');
        this.gaugeArc.setAttribute('stroke', this.valueArcData.color);
        this.gaugeArc.setAttribute('stroke-width', this.gaugeStrokeWidth);
        this.gaugeArc.setAttribute('transform', 'scale(-1, 1) translate(-110, 0)');

        let percentage = 0;
    percentage = this.valueArcData.value;

        this.gaugeArc.style.strokeDasharray = this._getArcLength(radius, 300, percentage);

        this.el.appendChild(this.gaugeArc);
    }

    // degree
    _degreesToRadians(degrees) {
        const pi = Math.PI;
        return degrees * (pi / 180);
    }
    // arc length
    _getArcLength(radius, degrees, val) {
        const radian = this._degreesToRadians(degrees);
        const arcLength = 2 * Math.PI * radius * (degrees / 360);
        const pathLength = arcLength * (val / 100);
        const dasharray = `${pathLength.toFixed(2)} ${arcLength.toFixed(2)}`;
        return dasharray;
    }



    // set gauge value
    setValue(v) {
            const baseValue = this.minmax[1] - this.minmax[0];
            const percentage = (v / baseValue) * 100;
            this.valueArcData.value = v;

            this.gaugeArc.style.strokeDasharray = this._getArcLength(this.gaugeRadius, 300, percentage);
            //gauge animation
            this.gaugeArc.style.transition = 'stroke-dasharray 1s ease-in-out';
    }

}


const arcGaugeData = {
  minmax: [0, 100],
  thresholdColor: ['#22b050', '#f4c141', '#e73621'],
  valueArcData: { type: 'arrow', color: '#3e3eff', value: 80 },
};
const arcGaugeIns = new arcGauge(document.querySelector('#chart'));
arcGaugeIns.init(arcGaugeData);

function changeArc(ipt) {
  arcGaugeIns.setValue(ipt.value);
}
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
<div class="container">
  <svg id="chart" xmlns="http://www.w3.org/2000/svg" width="180px" height="auto" viewBox="0 0 110 100"></svg>
  <input type="range"  min="0" max="100" step="1" value="80" onchange="changeArc(this)" />
</div>

如果这是不可能的,请告知是否有替代方案。

javascript html css svg automatic-ref-counting
1个回答
0
投票

主要思想是这样的: 您使用

getPointAtLength
计算仪表的终点,然后在该点移动箭头。

您还可以计算终点处路径的角度,以便您可以相应地旋转箭头。

为了计算角度,您需要 2 个点:

point1
即仪表的终点

point2
即稍微偏移的点 (
val + 0.1
)。

这将起作用 除非滑块的值为 100。在本例中,我使用箭头作为标记。

let length = chart.getTotalLength();
chart.style.strokeDasharray = length;
chart.style.strokeDashoffset = length;

function gauge(rangeValue) {
  let val = length - (length * rangeValue) / 100;
  if (rangeValue < 100 && rangeValue > 0) {
    let point1 = chart.getPointAtLength(val);
    let point2 = chart.getPointAtLength(val + 0.1);
    let angle = Math.atan2(point2.y - point1.y, point2.x - point1.x);
    arrow.setAttribute(
      "transform",
      "translate(" +
        [point1.x, point1.y] +
        ")" +
        "rotate(" +
        (angle * 180) / Math.PI +
        ")"
    );
  } else {
    chart.setAttribute("marker", "url(#m)");
  }
  chart.style.strokeDashoffset = val;
}

gauge(Number(range.value));

range.addEventListener("input", () => {
  let rangeValue = Number(range.value);
  gauge(rangeValue);
});
svg{border:solid;}
<svg xmlns="http://www.w3.org/2000/svg" width="180px" height="auto" viewBox="0 0 110 100">

  <path id="chart" d="M 34.00 91.37 A 42 42 0 1 1  76.00 91.37" fill="none" stroke="#3e3eff" stroke-width="6" transform="scale(-1, 1) translate(-110, 0)"  marker="url(#m)"></path>
  <marker id="m">
    <path id="markerarrow" fill="red" d="M0,0L0,-10L-14,0L0,10" />
  </marker>
  <use href="#markerarrow" id="arrow"/>
</svg>
</br>
<p><input type="range" id="range" min="0" max="100" step="1" value="80" /></p>

观察:这个解决方案并不完美。如果您快速移动滑块,您可能会得到一个偏移的箭头。

© www.soinside.com 2019 - 2024. All rights reserved.