How to Create Smooth Drag‑Slider Effects with SVG Bezier Curves

This article explains how to use SVG path commands and JavaScript to draw and animate Bézier curves for a fluid drag‑slider UI, covering background, curve theory, implementation steps, stroke‑dash techniques, offset‑path alternatives, and SMIL animation for interactive effects.

ELab Team
ELab Team
ELab Team
How to Create Smooth Drag‑Slider Effects with SVG Bezier Curves

Technical Sharing

This sharing covers the following aspects:

Background

Explanation of Bézier curves

Implementation and exploration process

Background

In a recent X‑business evaluation report page, a requirement emerged for users to drag a slider left or right to view information for each level.

Previously, the UI switched images, causing noticeable lag, especially because the level intervals are large.

To achieve a smoother effect, the solution uses SVG to draw Bézier curves, dynamically switching between solid and dashed lines and moving a hollow ball along the curve.

SVG path Tag

<svg>
  <path
    stroke="red"
    fill="none"
    d="M 4,130 C 12.85,129.1 45.3,126.7 63,124 C 80.7,121.3 104.3,116.5 122,112 C 139.7,107.5 163.3,100.3 181,94 C 198.7,87.7 222.3,78.1 240,70 C 257.7,61.9 281.3,49.9 299,40 C 316.7,30.1 349.15,9.4 358,4"></path>
</svg>

The following commands are used (uppercase letters denote absolute coordinates):

M – moveto (set start point)

L – lineto (draw a line)

H – horizontal lineto

V – vertical lineto

C – curveto (cubic Bézier)

S – smooth curveto

Q – quadratic Bézier

T – smooth quadratic Bézier

A – elliptical arc

Z – closepath

Using lowercase letters would create relative paths.

Bézier Curves

A Bézier curve consists of line segments and draggable control points that determine the curve’s direction. By adjusting four points (start, end, and two control points), the curve’s shape can be edited; the lines from the end points to the control points form the “control lines”.

First‑order Bézier Curve

Formula:

B(t) = P1 + (P2 − P1)·t = P1·(1−t) + P2·t,  t∈[0,1]

Second‑order Bézier Curve

With three points P1, P2, P3 (P2 is the control point):

M = P1·(1‑t) + P2·t

N = P2·(1‑t) + P3·t

B(t) = M·(1‑t) + N·t

Third‑order Bézier Curve

Formula (using points P0‑P3):

const calculateCirclePoint = (t, PointArray) => {
  const p0 = PointArray[0];
  const p1 = PointArray[1];
  const p2 = PointArray[2];
  const p3 = PointArray[3];
  const temp = 1 - t;
  const x = p0.x*temp*temp*temp + 3*p1.x*t*temp*temp + 3*p2.x*t*t*temp + p3.x*t*t*t;
  const y = p0.y*temp*temp*temp + 3*p1.y*t*temp*temp + 3*p2.y*t*t*temp + p3.y*t*t*t;
  return {x, y};
};

Determining Control Points in a Cubic Bézier

Direction: Ensure the first derivative (tangent) is continuous; the control‑point line’s slope matches the tangent.

Length: Controls curve “tightness”. A common heuristic is length = AC * 0.15 for smoothness.

For the start point A and end point D, the control points can be approximated by extending the line AB (or DC) with the calculated length.

Implementation Challenges

Switching between solid and dashed lines while dragging.

Moving a hollow ball along the curve in sync with the drag.

Animating the ball and curve after release.

SVG Basics – stroke-dasharray and stroke-dashoffset

stroke-dasharray

creates dashed lines, e.g., '10,5' means 10 px dash, 5 px gap, repeated. stroke-dashoffset shifts the dash pattern along the path; positive values move the pattern forward, negative values move it backward.

Example: codepen example

CSS offset-path and offset-distance

offset-path

defines a custom motion path. offset-distance specifies how far along that path the element moves (0‑100%).

Examples: codepen example

Compatibility

Implementation Process

Step 1 – Draw Bézier Curve

The slope of line BC matches the slope of line x1‑x2.

Calculate Circle Center and Handle Coordinates

// Dynamic circle center coordinates
const PointArray = [
  [4,130],[63,124],[122,112],[181,94],[240,70],[299,40],[358,4]
];

// Compute angle and length between two points
const line = (pointA, pointB) => {
  const lengthX = pointB[0] - pointA[0];
  const lengthY = pointB[1] - pointA[1];
  return {
    length: Math.sqrt(Math.pow(lengthX,2) + Math.pow(lengthY,2)),
    angle: Math.atan2(lengthY, lengthX)
  };
};

// Get control point for a segment
const controlPoint = (current, previous, next, reverse) => {
  const p = previous || current;
  const n = next || current;
  const l = line(p, n);
  const angle = l.angle + (reverse ? Math.PI : 0);
  const length = l.length * BezierCurveAndCirclePoint.smoothing;
  const x = current[0] + Math.cos(angle) * length;
  const y = current[1] + Math.sin(angle) * length;
  return [x, y];
};

const getBezierCurvePointArray = () => {
  const array = [];
  pointArray.forEach((item, i) => {
    if (i === 0) return;
    const cps = controlPoint(pointArray[i-1], pointArray[i-2], item, false);
    const cpe = controlPoint(item, pointArray[i-1], pointArray[i+1], true);
    array.push([
      {x: pointArray[i-1][0], y: pointArray[i-1][1]},
      {x: cps[0], y: cps[1]},
      {x: cpe[0], y: cpe[1]},
      {x: item[0], y: item[1]}
    ]);
  });
  return array;
};

Draw Three Cubic Bézier Curves

Observation: Switching the green solid line to a green dashed line is equivalent to moving the solid line left via stroke-dashoffset. The gray solid line turning green dashed moves right.

Reference implementation: codepen example

Step 2 – Compute Curve Length

Determine how far stroke-dashoffset should move when the user drags 2 px.

Initial stroke-dasharray value is set based on the sampled length.

Calculate Cubic Bézier Length

const cubicBezierLength = (PointArray, sampleCount) => {
  const ptCount = sampleCount || 40;
  let totDist = 0;
  let lastX = PointArray[0].x;
  let lastY = PointArray[0].y;
  for (let i = 1; i < ptCount; i++) {
    const pt = calculateCirclePoint(i / ptCount, PointArray);
    const dx = pt.x - lastX;
    const dy = pt.y - lastY;
    totDist += Math.sqrt(dx * dx + dy * dy);
    lastX = pt.x;
    lastY = pt.y;
  }
  const dx = PointArray[3].x - lastX;
  const dy = PointArray[3].y - lastY;
  totDist += Math.sqrt(dx * dx + dy * dy);
  return Math.floor(totDist);
};

Step 3 – Move Hollow Ball with Drag

Because offset-path is not supported on iOS, the ball’s coordinates are calculated in real time using the Bézier formula.

Step 4 – Animate Ball After Release

SVG SMIL Animation

<animateMotion/>
<circle
  opacity={circleAnimationPointOpacity ? '1' : '0'}
  r="3"
  fill="#ffffff"
  strokeWidth="2"
  stroke="#43E077">
  <animateMotion
    ref={circleAnimationRef}
    path={cirlceAnimationPath}
    dur="200ms"
    keySplines="0.25 0.1 0.25 1"
    fill="freeze"
    begin="indefinite"
    repeatCount="1"/>
</circle>

Reference: codepen example

References

https://codepen.io/Josh_byte/pen/MWQVWKK

https://codepen.io/Josh_byte/pen/PoQzjON

https://zhuanlan.zhihu.com/p/31242043

https://codepen.io/Josh_byte/pen/bGLvKYM

https://codepen.io/Josh_byte/pen/oNEYpjO

animationJavaScriptfrontend developmentSVGCSSBezier Curve
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.