Hey CodePen party people, today I want to talk to you about quadratic curves in the SVG path element.

There comes a time in every young (or old) person's life when you actually have to stop and look at what is going on inside your SVG and try to make sense of it. A quadratic curve path string looks something like this:

  <path d="M40,170 Q180,40 460,170" />

Which as you can see is just a bunch of letters and numbers.

If you’re tempted to bail right now I feel you. But stick with me and we can figure this thing out.

I’m going to break this down for you. I even created a little visual aid for you — with SVG! How meta of me.

The quadratic curve is made of three points. A start point, the control point (which controls the curve) and the end point. The points are specified as an x,y coordinate pair in the coordinate system created by the size and viewBox of the SVG. In the example above my SVG is 500px in width and 250px in height. My coordinate points are at (40px,170px), (250px, 40px - tweened to 180px, 40px) and (460px,170px). This draws a nice curve.

So what?

Why am I telling you about this anyhow? Well, once we understand how these curves work we can manipulate them with JavaScript, make magic happen in the browser and capture the hearts and minds of all those around us. Why do you think I have so many friends? It's because I know how to hand code SVG. Probably.

I’m going to show you how to make this rubbery text underline by manipulating an SVG path (I spotted this effect at panda.network. They use canvas for the effect but we’re going to use SVG because we are cool like that [sunglasses face emoji]).

Let's do this

Let’s talk through how we’re going to approach this. We need to make our path ‘bend’ back and forth. We know we can control the bend of the path by updating the control point (second set of coordinates) of the path. So let’s create a function that will update the string that controls our path.

  function updatePath(y) {
    // update SVG path control point
    path.setAttribute('d', 'M10,150 Q200,'+y+' 390,150');
}

okay, lets see how our function works. We’re just going to tween our control point back and forth, and see how the path bends with it.

Oooh. So bendy.

In the demo above, the path is controlled by pointer interaction. It acts sort of like a rubber band, where the pointer can stretch it to a certain point and then it will snap back in to position. So how do we create this effect?

First, we need to attach an event listener to our path so we can tell when the pointer is touching it. Remember, SVG paths are elements too so you can attach JavaScript event listeners to them just like regular DOM elements. We'll also add an event listener to the window to track mouse movement.

  path.addEventListener('mouseover', function() {
  // if we haven't connected yet and we're not tweening back to center, begin connection
  if (!connected && !tweening) {
    connected = true;
    svgElement.style.cursor = 'pointer';
  }
});

window.addEventListener('mousemove', function(e) {
// storing the y position of the mouse - we want the y pos relative to the SVG container so we'll subtract the container top from clientY.
    mousePos.y = e.clientY - svgTop;
});

When our pointer has connected with the path, we will update our control point so it looks like the path is bending from the pointer dragging it. We want the control point either a little higher or lower than the actual pointer position, because that will bring the curve up or down to where the pointer actually is.

  function updateCurve() {
  var y = mousePos.y;
  y = mousePos.y - (150-mousePos.y)*1.1;
  path.setAttribute('d', 'M10,150 Q200,'+y+' 390,150');
}

There has to come a point where the curve can bend no more and ‘snap’ back in to position. We’re going to check for this snap point, when it is reached we’ll detach the curve from the mouse movement and use an elastic-ease-out tween to make the curve spring back in to place.

  function updateCurve() {
  var y = mousePos.y;
  y = mousePos.y - (150-mousePos.y)*1.1;
  // check if we've reached our threshold
  if (Math.abs(150-y) > 100) {
    connected = false;
    tweening = true;
    svgElement.style.cursor = 'default';
    // snap it back
    snapBack(y);
  } else {
    path.setAttribute('d', 'M10,150 Q200,'+y+' 390,150');
  }
}

function snapBack(y) {
  tween = new TWEEN.Tween({ y: y })
    .to({ y: 150 }, 800)
    .easing( TWEEN.Easing.Elastic.Out )
    .onUpdate( function () {
      updatePath(this.y);
    }).onComplete(function() {
      tweening = false;
    }).start();
}

Throw some text on our underline and we are done.

Now you can bend SVG curves however you like, with JavaScript! Now that's pretty cool.


If you have a question or a topic you would like me to write about next, please leave a comment for me a below, send me a tweet at @rachsmithtweets or flick me an email at contact at rachsmith dot com.


10,644 6 91