Dynamic Shape Overlays with SVG

Some ideas for multi-layered SVG shape overlays that get generated dynamically with adjustable properties for a variety of effects.

Today we’d like to share another way of achieving morphing page transitions. This time, we’ll generate multiple SVG curves with JavaScript, making many different looking shapes possible. By controlling the individual coordinates of the several layers of SVG paths, the curved shapes animate to a rectangle (the overlay) with a gooey motion. We use some nice easing functions from glsl-easings and by tuning the curve, speed and the delay value, we can generate many interesting effects.

Attention: We use some new CSS properties in the demos; please view them with a modern browsers.

This demo is kindly sponsored by HelloSign API: The dev friendly eSign.

Building the SVG

Let’s have a look at the SVG which we will use to insert the path coordinates dynamically.
First, we’ll make sure that the whole SVG and the overlay paths are stretched to the size of the screen. For that, we’ll set the preserveAspectRatio attribute to none. Depending on how many layers we want, we’ll create that amount of paths:

<svg class="shape-overlays" viewBox="0 0 100 100" preserveAspectRatio="none">
  <path class="shape-overlays__path"></path>
  <path class="shape-overlays__path"></path>
  <path class="shape-overlays__path"></path>
</svg>

The styles that will allow the SVG to match the size of the browser window looks as follows:

.shape-overlays {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0; 
  left: 0;
}

Each path element corresponds to a layer of the overlay. We’ll specify the fill for each of these layers in our CSS. The last path element is the background that stays after the overlay expansion:

.shape-overlays path:nth-of-type(1) { fill: #c4dbea; }
.shape-overlays path:nth-of-type(2) { fill: #4c688b; }
.shape-overlays path:nth-of-type(3) { fill: #2e496a; }

Note that in our demos, we make use of CSS custom properties to set the path colors.

The JavaScript

For our demos, we define an overlay control class that allows us to set and control a couple of things. By changing each value, you can create unique looking shapes and effects:

class ShapeOverlays {
  constructor(elm) {
    this.elm = elm; // Parent SVG element.
    this.path = elm.querySelectorAll('path'); // Path elements in parent SVG. These are the layers of the overlay.
    this.numPoints = 18; // Number of control points for Bezier Curve.
    this.duration = 600; // Animation duration of one path element.
    this.delayPointsArray = []; // Array of control points for Bezier Curve.
    this.delayPointsMax = 300; // Max of delay value in all control points.
    this.delayPerPath = 60; // Delay value per path.
    this.timeStart = Date.now();
    this.isOpened = false;
  }
  ...
}
const elmOverlay = document.querySelector('.shape-overlays');
const overlay = new ShapeOverlays(elmOverlay);

Further methods that determine the appearance of the overlay are the ShapeOverlays.toggle() method and the ShapeOverlays.updatePath() method.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

The ShapeOverlays.toggle() method has the function of opening and closing the overlay, and also of setting the delay value of each control point for every time it opens and closes. It is not necessary to set the delay value every time, but by altering it, it will create some nice randomness.

The ShapeOverlays.updatePath() controls the animation by specifying the easing function.

For example, in demo 1, the same easing function is used for all control points, and the delay value is set like a fine wave using trigonometric functions, so that we get a “melting” appearance.

toggle() {
  const range = 4 * Math.random() + 6;
  for (var i = 0; i < this.numPoints; i++) {
    const radian = i / (this.numPoints - 1) * Math.PI;
    this.delayPointsArray[i] = (Math.sin(-radian) + Math.sin(-radian * range) + 2) / 4 * this.delayPointsMax;
  }
  ...
}

updatePath(time) {
  const points = [];
  for (var i = 0; i < this.numPoints; i++) {
    points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
  }
  ...
}

In our demos we use this effect to create an overlay in order to show a menu in the end of the animation. But it could also be used for other types of transitions, like page transitions or scroll effects. Your imagination is the limit.

Here are a couple of screenshots:

ShapeOverlays_01

ShapeOverlays_02

ShapeOverlays_03

ShapeOverlays_04

We hope you enjoyed this effect and find it useful!

Credits

  • glsl-easings by glslify. Easing functions that use to demos are based on the code of glsl-easings module.

Yoichi Kobayashi

I am a front-end developer. I wanna be a creative coder.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 21

Comments are closed.
  1. Great work on this one ! Love this animations, pretty fluid compared with other method ! Vizions agence de communication

  2. Awesome stuff. I want to use demo3 as a preloader. How do I make it show opened and when the page finishes loading I would like to close it?

  3. Awesome…another super stuff. I love the last demo. Thanks a lot for sharing with this world. Keep always rocking.

  4. First I got to say that I love this site. I’ve learned so much through the years with all the tutorials. But I miss the old days when you guys have more variety of content (The quality is still the same, pretty high!). Nowadays is all about css morphing. Not to criticize but may be this is the sentiment of many other users.

    That said, keep up the good work! Awesome post!

  5. It would be really nice if you seperated the css styles for each demo as well. It would make things way easier for us to implement.

  6. Can anyone please tell me how to make this work with a Adobe muse site.

    I am learning and although I understand MUSE inserting CSS and .JS scripts baffle me.
    Nothing I do works.

    Any help would be greatly appreicated

  7. This is causing an error on iPad. How to I rewrite it without the arrow function?

    requestAnimationFrame(() => {
    this.renderLoop();
    });

    • You write a normal anonymous `function() {}` and you reference `this` keyword outside like this: `var _this = this`.

  8. hello,
    when I give the menu link “section id like #about or #home ” menu don,t hide it only work when give page link like “index.html or about.html” …how can solve this
    thank you

  9. Hello. Amazing animation, I really appreciate it. Thanks a lot.
    how can i close the overlay when I click in a menu item ?
    Thanks.

  10. Hey, how’d you calculate/derive the trig functions in updatePaths() & toggle() functions? Was this done via trial & error?

    Also great work. This is super cool.

  11. When I do this, its only triggering on the first element of its class, any way to make the JS apply to all elements of the class or loop search? when i do inspect element edits on demo6, its able to apply to all hamburgers i create, i cant figure out whats causing it to not repeat