我正在使用 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>
如果这是不可能的,请告知是否有替代方案。
主要思想是这样的: 您使用
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>
观察:这个解决方案并不完美。如果您快速移动滑块,您可能会得到一个偏移的箭头。