用于范围输入的 SVG 圆弧滑块

Max*_*alo 4 javascript css svg slider

我的目标是设计一个看起来像这样的弧形滑块

在此输入图像描述

我有以下模板结构

<svg width="500" height="300">
  <path id="track" stroke="lightgrey" fill="transparent" stroke-width="20" d="
     M 50 50
     A 90 90 0 0 0 300 50
  "/>
  <path id="trackFill" fill="cyan" stroke-width="20" d="
     M 50 50
     A 90 90 0 0 0 [some dynamic value?] [some dynamic value?]
  "/>
  <circle id="knob" fill="lightblue"  cx="[dynamic, initial - 50]" cy="[dynamic, initial - 50]" r="25"/>
</svg>

Run Code Online (Sandbox Code Playgroud)

knob- 用户应该拖动以更改值的控件

track- 幻灯片的完整弧线

trackFill- 旋钮之前的滑块路径部分

trackFill当沿着滑块曲线拖动旋钮时,是否可以覆盖滑块之前的部分?如果是这样,哪些 API 或 CSS 规则可以帮助我实现这样的结果?

Pau*_*eau 7

你追求的是这样的东西吗?

let svg = document.getElementById("slider");
let trackFill = document.getElementById("trackFill");
let knob = document.getElementById("knob");
let isDragging = false;
let sliderDragOffset = {dx: 0, dy: 0};
let ARC_CENTRE = {x: 175, y: 50};
let ARC_RADIUS = 125;

let sliderValue = 0;
setSliderValue(sliderValue);


function setSliderValue(value)
{
  // Limit value to (0..sliderMax)
  let sliderMax = track.getTotalLength();
  sliderValue = Math.max(0, Math.min(value, sliderMax));
  // Calculate new position of knob
  let knobRotation = sliderValue * Math.PI / sliderMax;
  let knobX = ARC_CENTRE.x - Math.cos(knobRotation) * ARC_RADIUS;
  let knobY = ARC_CENTRE.y + Math.sin(knobRotation) * ARC_RADIUS;
  // Adjust trackFill dash patter to only draw the portion up to the knob position
  trackFill.setAttribute("stroke-dasharray", sliderValue + " " + sliderMax);
  // Update the knob position
  knob.setAttribute("cx", knobX);
  knob.setAttribute("cy", knobY);
}


knob.addEventListener("mousedown", evt => {
  isDragging = true;
  // Remember where we clicked on knob in order to allow accurate dragging
  sliderDragOffset.dx = evt.offsetX - knob.cx.baseVal.value;
  sliderDragOffset.dy = evt.offsetY - knob.cy.baseVal.value;
  // Attach move event to svg, so that it works if you move outside knob circle
  svg.addEventListener("mousemove", knobMove);
  // Attach move event to window, so that it works if you move outside svg
  window.addEventListener("mouseup", knobRelease);
});


function knobMove(evt)
{
  // Calculate adjusted drag position
  let x = evt.offsetX + sliderDragOffset.dx;
  let y = evt.offsetY + sliderDragOffset.dy;
  // Position relative to centre of slider arc
  x -= ARC_CENTRE.x;
  y -= ARC_CENTRE.y;
  // Get angle of drag position relative to slider centre
  let angle = Math.atan2(y, -x);
  // Positions above arc centre will be negative, so handle them gracefully
  // by clamping angle to the nearest end of the arc
  angle = (angle < -Math.PI / 2) ? Math.PI : (angle < 0) ? 0 : angle;
  // Calculate new slider value from this angle (sliderMaxLength * angle / 180deg)
  setSliderValue(angle * track.getTotalLength() / Math.PI);
}


function knobRelease(evt)
{
  // Cancel event handlers
  svg.removeEventListener("mousemove", knobMove);
  window.removeEventListener("mouseup", knobRelease);
  isDragging = false;
}
Run Code Online (Sandbox Code Playgroud)
<svg id="slider" width="500" height="300">
  <g stroke="lightgrey">
    <path id="track" fill="transparent" stroke-width="20" d="
       M 50 50
       A 125 125 0 0 0 300 50
    "/>
  </g>
  <use id="trackFill" xlink:href="#track" stroke="cyan"/>
  <circle id="knob" fill="lightblue" cx="50" cy="50" r="25"/>
</svg>
Run Code Online (Sandbox Code Playgroud)

为了清晰起见,我使这段代码保持简单,但代价是存在一些限制。

它假设每页只有一个滑块。如果您需要更多,则必须将滑块特定值(例如 sliderValue 和 isDragging)分开。您可以为此使用数据属性。您还需要从通过id属性访问 SVG 元素切换到另一种方式(例如class属性),因为 id 属性在页面上必须是唯一的。

  • 或者将其包装在 W3C 标准 Web 组件中以在一个页面上使用多个实例 (2认同)