Pedro Fragata
Pedro Fragata

Reputation: 43

How to expand a SVG responsively?

I'm using a SVG under an hamburger menu so when the user clicks that menu, the SVG expands and fills the entire screen with the options on it. I'm using scale() for that so basically what happens is that the SVG has a scale of 1 and when the user clicks on the menu the SVG gets a scale(35).

Now the problem. That scale(35) doesn't fill the entire screen on bigger resolutions or even on some mobile resolutions with bigger height. If I set the scale for something that will ensure me that will cover most of the screens, like scale(70), this will make the close animation really messy for going through such a big scale to 1. How can I expand this SVG with a smooth animation so it fills every type of screen?

EDIT - Added code with a simple and fast example downthere

Below are some images that explain what happens.

Closed menu / SVG with scale(1) Close menu / SVG with scale(1)

Opened menu / SVG with scale(70) Opened menu / SVG with scale(70)

Menu closing / SVG going from scale(70) to scale(1) and looking messy Menu closing / SVG going from scale(70) to scale(1) and looking messy

function menuOnClick() {
  document.getElementById("menu-bar").classList.toggle("change");
  document.getElementById("nav").classList.toggle("change");
  document.getElementById('blob').classList.toggle("change-bg");
}
#menu {
  z-index: 2;
}

#menu-bar {
  width: 45px;
  height: 40px;
  margin: 30px 0 20px 20px;
  cursor: pointer;
  float: right;
}

.bar {
  height: 5px;
  width: 100%;
  background-color: #00d1a9;;
  display: block;
  border-radius: 5px;
  transition: 0.3s ease;
}

#bar1 {
  transform: translateY(-4px);
}

#bar3 {
  transform: translateY(4px);
}

.nav {
  transition: 0.3s ease;
  display: none;
}

.nav ul {
  padding: 0 22px;
}

.nav li {
  list-style: none;
  padding: 12px 0;
}

.nav li a {
  color: white;
  font-size: 20px;
  text-decoration: none;
}

.nav li a:hover {
  font-weight: bold;
}

.menu-bg, #menu {
  top: 0;
  left: 0;
  position: absolute;
}

.change {
  display: block;
}

.change .bar {
  background-color: white;
}

.change #bar1 {
  transform: translateY(4px) rotateZ(-45deg);
}

.change #bar2 {
  opacity: 0;
}

.change #bar3 {
  transform: translateY(-6px) rotateZ(45deg);
}

.change-bg {
  transform: scale(70);
}
.blob {
  margin-top:-32px;
  top: 0;
  z-index: 1;
  pointer-events: none;
  -webkit-transition: all 0.75s linear;
  transition: all 0.75s linear;
  display: block;
  will-change: auto;
    filter: none;
  -webkit-filter: blur(0);
  -moz-filter: blur(0);
  -ms-filter: blur(0);
  filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='0');
}
<div id="menu">
  <div id="menu-bar" onclick="menuOnClick()">
    <div id="bar1" class="bar"></div>
    <div id="bar2" class="bar"></div>
    <div id="bar3" class="bar"></div>
  </div>
  <nav class="nav" id="nav">
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Contact</a></li>
      <li><a href="#">Blog</a></li>
    </ul>
  </nav> 
</div>
                                <svg class="blob" id="blob" xmlns="http://www.w3.org/2000/svg" width="265.42" height="151.973" viewBox="0 0 265.42 151.973">
                                    <title>Menu Blob</title>
                                    <desc>The blob that grows to be the menu background</desc>
                                    <path class="blobPath" id="blobPath" shape-rendering="auto" d="M-1377.154,10877.442c-10.882-7.812-24.262-11.1-36.627-16.251s-24.764-13.355-28.853-26.112c-.135.766,251.322-30.752,251.3-30.855s-9.766,33.5-15.478,42.831c-9.83,16.055-20.015,32.053-32.926,45.756a125.25,125.25,0,0,1-18.85,16.492,89.6,89.6,0,0,1-28.133,13.538,70.507,70.507,0,0,1-29.47,1.6,56.7,56.7,0,0,1-24.487-10C-1354.7,10904.193-1363.044,10887.567-1377.154,10877.442Z" transform="translate(2763.902 -10547.315) rotate(7)" />
                                </svg>

<div class="menu-bg" id="menu-bg"></div>

Upvotes: 4

Views: 166

Answers (2)

Paul LeBeau
Paul LeBeau

Reputation: 101800

Here's a different approach.

document.getElementById("menu-bar").addEventListener('click', function(evt) {
  evt.target.parentElement.classList.toggle("open");
});
body, html {
  margin: 0;
}

#menu {
  position: relative;
}



#blob {
  position: absolute;
  top: 0;
  right: 0;
  transition: 0.3s ease;
  z-index: -1;
}

#blob g {
  fill: #00d1a9;
  transition: 0.3s ease;
  transform-origin: 150px 32px;
  transform-box: fill-box;
}

.open #blob {
  width: 100%;
  height: 100%;
}

.open #blob g {
  transform: scale(20);
}


#menu-bar {
  position: absolute;
  top: 40px;
  right: 100px;
  width: 45px;
  height: 40px;
  cursor: pointer;
}

.bar {
  height: 5px;
  width: 100%;
  background-color: rgba(255, 255, 255, 0.8);
  display: block;
  border-radius: 5px;
  transition: 0.3s ease;
  pointer-events: none;
}

#bar1 {
  transform: translateY(-4px);
}

#bar3 {
  transform: translateY(4px);
}

.open #bar1 {
  transform: translateY(4px) rotateZ(-45deg);
}

.open #bar2 {
  opacity: 0;
}

.open #bar3 {
  transform: translateY(-6px) rotateZ(45deg);
}




.nav {
  display: inline-block;
  opacity: 0;
  pointer-events: none;
}

.nav ul {
  padding: 0 22px;
  margin: 0;
}

.nav li {
  list-style: none;
  padding: 12px 0;
}

.nav li a {
  color: black;
  font-size: 20px;
  text-decoration: none;
}

.nav li a:hover {
  font-weight: bold;
}

.open .nav {
  transition: 0.3s ease;
  transition-delay: 0.2s;
  opacity: 1;
  pointer-events: auto;
}
<div id="menu">
  <svg class="blob" id="blob" width="256" height="108" viewBox="10 31 256 108">
    <g>
      <path class="blobPath" id="blobPath" shape-rendering="auto" d="M-1377.154,10877.442c-10.882-7.812-24.262-11.1-36.627-16.251s-24.764-13.355-28.853-26.112c-.135.766,251.322-30.752,251.3-30.855s-9.766,33.5-15.478,42.831c-9.83,16.055-20.015,32.053-32.926,45.756a125.25,125.25,0,0,1-18.85,16.492,89.6,89.6,0,0,1-28.133,13.538,70.507,70.507,0,0,1-29.47,1.6,56.7,56.7,0,0,1-24.487-10C-1354.7,10904.193-1363.044,10887.567-1377.154,10877.442Z" transform="translate(2763.902 -10547.315) rotate(7)" />
    </g>
  </svg>

  <div id="menu-bar">
    <div id="bar1" class="bar"></div>
    <div id="bar2" class="bar"></div>
    <div id="bar3" class="bar"></div>
  </div>

  <nav class="nav" id="nav">
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Contact</a></li>
      <li><a href="#">Blog</a></li>
    </ul>
  </nav> 
</div>

Upvotes: 3

A Haworth
A Haworth

Reputation: 36426

The question is how to scale the blob responsively rather than have just one scale value which could be too small for very large screens, and conversely cause problems on very small ones when shrinking.

As JS is already being invoked on click we can get the base-size of the blob and see 'how many times' it will fit into the window height and width respectively and use those ratios to calculate a scale that will guarantee the window becomes covered but without huge scaling.

We can do this by calculating a CSS variable in the click event function which will then get used in the change-bg class (but will be ignored when this class is not set). This has the advantage that it will automatically recalculate scale if the window has been resized.

function menuOnClick() {
  document.getElementById("menu-bar").classList.toggle("change");
  document.getElementById("nav").classList.toggle("change");
  document.getElementById('blob').classList.toggle("change-bg");
  /* added */
  let blob = document.getElementById('blob');
  let rect = blob.getBoundingClientRect();
  document.getElementById('blob').style.setProperty('--scale', Math.max(4*window.innerWidth/rect.width,4*window.innerHeight/rect.height));
}
#menu {
  z-index: 2;
}

#menu-bar {
  width: 45px;
  height: 40px;
  margin: 30px 0 20px 20px;
  cursor: pointer;
  float: right;
}

.bar {
  height: 5px;
  width: 100%;
  background-color: #00d1a9;;
  display: block;
  border-radius: 5px;
  transition: 0.3s ease;
}

#bar1 {
  transform: translateY(-4px);
}

#bar3 {
  transform: translateY(4px);
}

.nav {
  transition: 0.3s ease;
  display: none;
}

.nav ul {
  padding: 0 22px;
}

.nav li {
  list-style: none;
  padding: 12px 0;
}

.nav li a {
  color: white;
  font-size: 20px;
  text-decoration: none;
}

.nav li a:hover {
  font-weight: bold;
}

.menu-bg, #menu {
  top: 0;
  left: 0;
  position: absolute;
}

.change {
  display: block;
}

.change .bar {
  background-color: white;
}

.change #bar1 {
  transform: translateY(4px) rotateZ(-45deg);
}

.change #bar2 {
  opacity: 0;
}

.change #bar3 {
  transform: translateY(-6px) rotateZ(45deg);
}

.blob {
  transform: scale(1); /* added */
  margin-top:-32px;
  top: 0;
  z-index: 1;
  pointer-events: none;
  -webkit-transition: all 0.75s linear;
  transition: all 0.75s linear;
  display: block;
  will-change: auto;
    filter: none;
  -webkit-filter: blur(0);
  -moz-filter: blur(0);
  -ms-filter: blur(0);
  filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='0');
}
.change-bg { /* moved after .blob so takes precedence */
  transform: scale(var(--scale));/* Changed from 70 */
}
<div id="menu">
  <div id="menu-bar" onclick="menuOnClick()">
    <div id="bar1" class="bar"></div>
    <div id="bar2" class="bar"></div>
    <div id="bar3" class="bar"></div>
  </div>
  <nav class="nav" id="nav">
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Contact</a></li>
      <li><a href="#">Blog</a></li>
    </ul>
  </nav> 
</div>
                                <svg class="blob" id="blob" xmlns="http://www.w3.org/2000/svg" width="265.42" height="151.973" viewBox="0 0 265.42 151.973">
                                    <title>Menu Blob</title>
                                    <desc>The blob that grows to be the menu background</desc>
                                    <path class="blobPath" id="blobPath" shape-rendering="auto" d="M-1377.154,10877.442c-10.882-7.812-24.262-11.1-36.627-16.251s-24.764-13.355-28.853-26.112c-.135.766,251.322-30.752,251.3-30.855s-9.766,33.5-15.478,42.831c-9.83,16.055-20.015,32.053-32.926,45.756a125.25,125.25,0,0,1-18.85,16.492,89.6,89.6,0,0,1-28.133,13.538,70.507,70.507,0,0,1-29.47,1.6,56.7,56.7,0,0,1-24.487-10C-1354.7,10904.193-1363.044,10887.567-1377.154,10877.442Z" transform="translate(2763.902 -10547.315) rotate(7)" />
                                </svg>

<div class="menu-bg" id="menu-bg"></div>

Note: this snippet seems to work on different window sizes OK, but in the 'emulator' for mobile on Edge in Windows10 I noticed a single little jerk when shrinking. I cannot explain this. Hope someone can.

Upvotes: 3

Related Questions