2

I am working with a svg element and I want to animate the height increasing upwards of these elements. But they are growing downward from the top probably due to SVG coordinate system. How can I make it go upwards from the bottom?

window.onload = function () {
    var x1 = document.querySelector('svg').viewBox.baseVal.height;
    var a = document.getElementById('wrapper1');
    var bgArray = [];
    for (var i = 1; i < a.children.length; i++) {
        bgArray.push((a.children[i].getBBox().height / x1) * 100);
    };
    bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--h1", x + '%'));
    bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--del", (i + 1) + 's')); //for staggered
}
.r1,
.r2 {    
    visibility: hidden;
    animation: moveHeight 2s ease-in var(--del) 1 forwards;
}

@keyframes moveHeight {
    0% {
        visibility: visible;
        height: 0%;
    }
    100% {
        visibility: visible;
        height: var(--h1);
    }
}
<!DOCTYPE html>
<html>

<body>

    <link rel="stylesheet" href="style.css">
    </link>

    <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">

      <g class="wrapper1" id="wrapper1" >
    <rect x="10" y="20" width="120" height="120" stroke="black" fill="none"></rect>   
   <rect class="r1" id="r1" x="10" y="80" width="20" height="60" stroke="none" fill="orange"></rect>
    <rect class="r2" id="r2" x="31" y="100" width="20" height="40" stroke="none" fill="green"></rect>
  <!----> 
  </g>
  
  
  </svg>
    <script src="index.js"></script>
</body>

</html>
smpa01
  • 4,149
  • 2
  • 12
  • 23
  • you can animate a linear gradient as the fill of the rect: https://stackoverflow.com/questions/42969478/svg-fill-animation-for-the-given-path. Also you can animate a very thick line clipped by the rects – enxaneta Mar 03 '22 at 08:28

3 Answers3

0

You could draw the bars but cover them and then gradually uncover them.

This covers them by adding white bars and shrinking those.

window.onload = function() {
  var x1 = document.querySelector('svg').viewBox.baseVal.height;
  var a = document.getElementById('wrapper1');
  var bgArray = [];
  for (var i = 1; i < a.children.length; i++) {
    bgArray.push((a.children[i].getBBox().height / x1) * 100);
  };
  bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--h1", x + '%'));
  bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--del", (i + 1) + 's')); //for staggered
}
.shrink {
  animation: moveHeight 2s ease-in var(--del) 1 forwards;
}

@keyframes moveHeight {
  0% {
    height: var(--h1);
  }
  100% {
    height: 0;
  }
}
<!DOCTYPE html>
<html>

<body>

  <link rel="stylesheet" href="style.css">
  </link>

  <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">

      <g class="wrapper1" id="wrapper1" >
    <rect x="10" y="20" width="120" height="120" stroke="black" fill="none"></rect>   
   <rect class="r1" id="r1" x="10" y="80" width="20" height="60" stroke="none" fill="orange"></rect>
   <rect class="r1 shrink"  x="10" y="80" width="20" height="60" stroke="none" fill="white"></rect>
    <rect class="r2" id="r2" x="31" y="100" width="20" height="40" stroke="none" fill="green"></rect>
    <rect class="r2 shrink"  x="30" y="99" width="22" height="41" stroke="none" fill="white"></rect>
  <!----> 
  </g>
  
  
  </svg>
  <script src="index.js"></script>
</body>

</html>

Note, at some zoom levels a screen pixel of the underneath bars can be left showing (giving a faint vertical line at the start). To get round that the second white bar has been moved one left and made one wider (and similarly for height). Somewhat hacky so I hope someone can come up with a more straightforward solution.

A Haworth
  • 30,908
  • 4
  • 11
  • 14
0

I have looked into this brilliant answer but I had trouble visualizing and adapting it to my scenario. So after I figured out, I am trying to visually explain and show how to adapt to different scenarios.

The basic markup is following

<!DOCTYPE html>
<html>

<body>

    <link rel="stylesheet" href="style.css">
    </link>

    <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
    
    <rect class="wrapper1" id="wrapper1" x="10" y="345" width="120" height="180" stroke="teal" fill="none"></rect>
    <line class="upper clone" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(110, 31, 194);stroke-width:1" /> 
    <g class="elements">
    <line class="upper" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(87, 202, 91);stroke-width:1"/> 
    <rect class="r0" id="r0" x="10" y="80" width="83" height="180" stroke="brown" fill="none"></rect>
    <rect class="r1" id="r1" x="10" y="80" width="20" height="60"  stroke="none"  fill="orange"></rect>
    <rect class="r2" id="r2" x="31" y="80" width="20" height="10"  stroke="none"  fill="green"></rect>
    <rect class="r3" id="r3" x="52" y="80" width="20" height="100" stroke="none"  fill="blue"></rect>
    <rect class="r4" id="r4" x="73" y="80" width="20" height="140" stroke="none"  fill="magenta"></rect>
    <line class="lower" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(87, 202, 91);stroke-width:1"/> 
  </g>
  <line class="lower clone" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(218, 149, 22);stroke-width:1" /> 
 
  <g class="wrapper2" id="wrapper2">
    <rect x="140" y="0" width="50" height="700" stroke="red" fill="none"></rect>

  </g>
  
  </svg>

    <script src="index2.js"></script>
</body>

</html>

For ease of understanding, I have created the two lines called upper and lower which are the start and end of all the rects. I have also cloned those lines where the transform will not apply.

The thumb rule of converting svg coordinate system to cartesian coordinate is to use this transform="translate(0, maxY) scale(1, -1)".

The maxY is actually max total of (y+height). In this case it is 260. now this value can be manipulated to correctly place the rects prior to animation

<g class="elements" transform="translate(0,260) scale(1, -1)"> positions

S1

<g class="elements" transform="translate(0,340) scale(1, -1)"> 260+rectY(80)=340 positions

S2

<g class="elements" transform="translate(0,425) scale(1, -1)"> 260+rectY(80)+(wrapperY 345 -260) positions

S3

and lastly positions at the bottom of the wrapper

<g class="elements" transform="translate(0,605) scale(1, -1)"> 260+rectY(80)+(wrapperY 345 -260)+wrapperHeight 180 = 605

S4

Putting everything together with javascript and css

//converting svg coordinates to cartesian coordinates
// transform="translate(0, maxY) scale(1, -1)".
// or transform="scale(1, -1) translate(0, -maxY)".


window.onload = function app() {
    var bgArray1 = []; //to collect only height
    var bgArray2 = []; //to collect height + y    
    var wrapperElement = document.getElementById('wrapper1');
    var wrapperY = wrapperElement.getBBox().y //what is the y coordinate of wrapper rect
    var wrapperHeight = wrapperElement.getBBox().height;

    var getRect = document.querySelectorAll('[class^="r"]');

    for (var i = 0; i < getRect.length; i++) {
        var _bBox = getRect[i].getBBox(); // getting the bBox for each rect
        var _height = _bBox.height + _bBox.y //getting each rect's height
        var _y = _bBox.y // getting each rect's y coordinate
        var _heightAndY = _height + _y //total of Y+height of each rect        
        bgArray1.push(_height);
        bgArray2.push(_heightAndY);


    }
    var _maxHeight = Math.max(...bgArray1); // use this if you want to position the rect at (y=0)
    var _maxTotal = Math.max(...bgArray2); // use this if you want to position the rect at (y=0)
    var _wrapperTop = _maxTotal + (wrapperY - _maxHeight); //use this if you want to postion the rect at the the 
    //top of the wrapper
    var _wrapperBottom = _wrapperTop + wrapperHeight //use this if you wantto position the rects at the 
        //bottom of the wrapper                                                    



    bgArray1.forEach((x, i) => getRect[i].setAttribute("transform", `translate(0,${_wrapperBottom}) scale(1,-1)`));
    bgArray1.forEach((x, i) => getRect[i].style.setProperty("--del", (i + 1) + 's'))


}
[class^="r"] {
    visibility: hidden;
    animation: moveHeight 2s ease-out var(--del) 1 forwards;
}

@keyframes moveHeight {
    0% {
        visibility: visible;
        height: 0
    }
    100% {
        visibility: visible;
        height: 100;
    }
}
<!DOCTYPE html>
<html>

<body>

    <link rel="stylesheet" href="style.css">
    </link>

    <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
    
    <rect class="wrapper1" id="wrapper1" x="10" y="345" width="120" height="180" stroke="teal" fill="none"></rect>
    <line class="upper clone" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(110, 31, 194);stroke-width:1" /> 
    <g>
    <line class="upper" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(87, 202, 91);stroke-width:1"/> 
    <rect class="r0" id="r0" x="10" y="80" width="83" height="180" stroke="brown" fill="none"></rect>
    <rect class="r1" id="r1" x="10" y="80" width="20" height="60"  stroke="none"  fill="orange"></rect>
    <rect class="r2" id="r2" x="31" y="80" width="20" height="10"  stroke="none"  fill="green"></rect>
    <rect class="r3" id="r3" x="52" y="80" width="20" height="100" stroke="none"  fill="blue"></rect>
    <rect class="r4" id="r4" x="73" y="80" width="20" height="140" stroke="none"  fill="magenta"></rect>
    <line class="lower" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(87, 202, 91);stroke-width:1"/> 
  </g>
  <line class="lower clone" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(218, 149, 22);stroke-width:1" /> 

  <g class="wrapper2" id="wrapper2">
    <rect x="140" y="0" width="50" height="700" stroke="red" fill="none"></rect>

  </g>
  
  </svg>
    <!--<script src="index.js"></script>-->
    <script src="index2.js"></script>
</body>

</html>
smpa01
  • 4,149
  • 2
  • 12
  • 23
0

Pure svg solution

The idea is to first hide the rectangles behind a masking strip.

Then raise them to the top by animating the y attribute of the rectangle

<animate id="anR1" xlink:href="#r1" attributeName="y" begin="Layer_1.click" 
  dur="1s" from="140" to="80"  repeatCount="1" fill="freeze" /> 

<!DOCTYPE html>
<html>

<body>

    <link rel="stylesheet" href="style.css">
    </link>

    <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">

      <g class="wrapper1" id="wrapper1" >
    <rect x="10" y="20" width="120" height="120" stroke="black" fill="none"></rect>   
   <rect class="r1" id="r1" x="10" y="140" width="20" height="60" stroke="none" fill="orange"></rect>
    <rect class="r2" id="r2" x="31" y="140" width="20" height="40" stroke="none" fill="green"></rect>
     <rect class="r3" id="r3" x="52" y="140" width="20" height="80" stroke="none" fill="dodgerblue"></rect>
         <!-- masking strip        -->
       <rect x="10" y="140" width="120" height="120"  fill="white"></rect> 
  </g> 
     <animate id="anR1" xlink:href="#r1" attributeName="y" begin="Layer_1.click" dur="0.5s" from="140" to="80"  repeatCount="1" fill="freeze" /> 
       <animate id="anR2" xlink:href="#r2" attributeName="y" begin="anR1.end+0.25s" dur="0.5s" from="140" to="100" repeatCount="1" fill="freeze" /> 
          <animate id="anR3" xlink:href="#r3" attributeName="y" begin="anR2.end+0.25s" dur="0.5s" from="140" to="60"  repeatCount="1" fill="freeze" />
    
  </svg>
    <script src="index.js"></script>
</body>

</html>
Alexandr_TT
  • 13,635
  • 3
  • 27
  • 54