Home > Blockchain >  how to layout items evenly with overlap
how to layout items evenly with overlap

Time:03-12

I want to layout list items evenly across a vertical container. All items have the same height (the last one has padding).

Normally I would use flex, but here is the challenge: The container expands with a transition to its final height - which is exactly the size of all li items stacked up with no overlap. In other words, flex is not relevant since the container height will forever be <= height of each item * the number of items.

What I need, is for the items to always spread out evenly, overlapping each other while filling up the container (starting from a complete overlap until they finally are stacked as they would with no intervention).

Here is a sandbox link to the setup of the problem: https://codesandbox.io/s/holy-water-uln898?file=/index.html

Would appreciate your help!

const expand = () => {
        document.getElementsByTagName("ul")[0].classList.add("expanded");
        lis = document.getElementsByTagName("li")
        
        
        for (let i = 1; i < lis.length; i  ){
        lis[i].style.top= i*25   "px"
        lis[i].classList.add("expand");   
        
        
        }
      };
ul {
        padding: 0;
        border: 3px solid;
        height: calc(25px   8px);
        transition: height 2s;
        position:relative;
      }

      ul.expanded {
        height: calc((25px * 3)   8px);
        
      }

      li {
        list-style: none;
        height: 25px;
        position:absolute;
        left:0;
        top:0;
        width:100%
      }

      li:last-child {
        padding-bottom: 8px;
      }

      li.item1 {
        background: red;
        color: darkred;
        
      }

      li.item2 {
        background: green;
        color: darkgreen;
      }

      li.item3 {
        background: yellow;
        color: orange;
      }

      button {
        position: absolute;
        top: 150px;
      }
      
      li.expand{      
      transition:top 2s;
      }
      
     
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Static Template</title>

    
  </head>
  <body>
    <ul>
      <li >
        item1
      </li>
      <li >
        item2
      </li>
      <li >
        item3
      </li>
    </ul>
    <button onClick="expand();">Click</button>

    
  </body>
</html>

CodePudding user response:

You can accomplish this with a bunch of wrappers. The list items are wrapped in an item with 0 height. Flex won't stack all the 0-height elements on top of each other unless its height is also 0 (because we must justify-content: space-between to distribute them when height is greater than 0), so the inner flex container has a height of 0 when closed, and the outer visible container (with the border) just adds a single items-worth of height to flex-container. The javascript is only for the demo.

This only works if every item has a known, static height. Transitioning height to auto is not easy by itself, let alone after you throw in some fancy overlapping.

let fullHeight = true
const button = document.querySelector(".button")
const container = document.querySelector(".container-restraint")
button.addEventListener("click", e => {
  if (fullHeight) container.className = "container-restraint closed"
  else container.className = "container-restraint"
  fullHeight = !fullHeight
})
.list-item {
  height: 100px; /* ITEM-HEIGHT */
  width: 200px; /* ITEM-WIDTH */
}

.item-1 { background-color: #ff000066; }
.item-2 { background-color: #00ff0066; }
.item-3 { background-color: #0000ff66; }
.item-4 { background-color: #ffff0066; }

.container-restraint {
  height: 300px; /* ITEM-HEIGHT * (N_ITEMS - 1) */
  width: 200px; /* ITEM-WIDTH */
  display: flex;
  flex-flow: column;
  transition: height 3s;
  overflow: visible;
  justify-content: space-between;
}

.container-restraint.closed {
  height: 0;
}

.expanding-container {
  width: max-content;
  border: 3px solid black;
  overflow: hidden;
  padding-bottom: 100px; /* ITEM_HEIGHT */
}

.wrapper {
  height: 0;
  overflow: visible;
  padding: 0;
}
<button >transition</button>
<div >
  <div >
    <div >
      <div ></div>
    </div>
    <div >
      <div ></div>
    </div>
    <div >
      <div ></div>
    </div>
    <div >
      <div ></div>
    </div>
  </div>
</div>

CodePudding user response:

You could use position: absolute; and jQuery animations to achieve the overlapping effect, like this:

const expand = () => {
        document.getElementsByTagName("ul")[0].classList.add("expanded");
        $('li').each(function(index, li){
          $(li).animate({
            top: (index * 25)   'px'
          }, 2000);
        });
      };
ul {
        padding: 0;
        border: 3px solid;
        height: calc(25px   8px);
        transition: height 2s;
        position: relative;
      }

      ul.expanded {
        height: calc((25px * 3)   8px);
      }

      li {
        list-style: none;
        height: 25px;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
      }

      li:last-child {
        padding-bottom: 8px;
      }

      li.item1 {
        background: red;
        color: darkred;
      }

      li.item2 {
        background: green;
        color: darkgreen;
      }

      li.item3 {
        background: yellow;
        color: orange;
      }

      button {
        position: absolute;
        top: 150px;
      }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
      <li >
        item1
      </li>
      <li >
        item2
      </li>
      <li >
        item3
      </li>
    </ul>
    <button onClick="expand();">Click</button>

  • Related