Search code examples
javascriptcssflexboxresponsive-designthumbnails

Movement of tiles between "mosaic of tiles" and "one tile zoomed and others as thumbnails"


I have an interface with a few panels, like here with a mosaic of 6 panels, using multiple display: flex div, with flex-direction column or row.

I'd like that, when clicking/touching on one panel, this one becomes nearly full size, and the other panels are in a left column as "thumbnails".

I don't see how I can make this with my current design with multiple flex elements inside each other.

Question: how do you create such a "click on one tile to zoom it and move other tiles as a column of thumbnails" feature?

enter image description here

.horizontal { display: flex; align-items: center; justify-content: center; gap: 1em; }
.vertical { display: flex; align-items: center; justify-content: space-around; flex-direction: column; gap: 1em; }
.box { width: 150px; height: 100px; background-color: #eee; }
<div class="horizontal">
    <div class="vertical">
        <div class="box">A</div>
        <div class="box">B</div>
    </div>
    <div class="vertical">
        <div class="box">C</div>
        <div class="box">D</div>
    </div>
    <div class="vertical">
        <div class="box">E</div>
        <div class="box">F</div>
    </div>    
</div>


Solution

  • You can use grid and use the grid-template-areas property to put a space where you need the larger image. I've put the skeleton of one up for you but you'll need to neaten up the box sizes a bit.

    window.onload = () => {
      document.querySelector('body').addEventListener('click', (event) => {
        document.querySelectorAll('.container > div').forEach((element) => {
          element.classList.remove('clicked'); 
        });
        document.querySelector('.container').classList.remove('clicked');
        
        const clickedElement = event.target;
        if(clickedElement.tagName=="IMG") {
          clickedElement.closest('.container').classList.add('clicked');    
          event.target.closest('.box').classList.add('clicked');
        }
      });
    }
    body {
      height: 100vh;
    }
    
    .container {
      display: inline-grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 0.5rem;
    }
    
    .container img {
      width: 100%;
    }
    
    .container.clicked {
      grid-template-columns: repeat(2, fit-content(0));
      grid-template-areas: ". img" ". img" ". img" ". img" ". img" ". img";
    }
    
    .box.clicked {
      grid-area: img;
      width: 450px;
    }
    
    .box.clicked img {
      width: 100%;
      animation: scalein 0.5s;
    }
    
    .box img {
      animation: scaleout 0.3s;
    }
    
    .container.clicked .box:not(.clicked) {
      grid-auto-flow: column;
      width: 80px;
    }
    
    @keyframes scalein {
      0% {
        transform: scale(50%);
        opacity: 0;
      }
      90% {
        transform: scale(102%);
      }
      100% {
        transform: scale(100%);
        opacity: 1;
      }
    }
    
    @keyframes scaleout {
      0% {
        transform: scale(150%);
      }
      100% {
        transform: scale(100%);
      }
    }
    <div class="container">
      <div class="box"><img src='https://picsum.photos/id/237/200'></div>
      <div class="box"><img src='https://picsum.photos/id/242/200'></div>
      <div class="box"><img src='https://picsum.photos/id/241/200'></div>
      <div class="box"><img src='https://picsum.photos/id/240/200'></div>
      <div class="box"><img src='https://picsum.photos/id/238/200'></div>
      <div class="box"><img src='https://picsum.photos/id/239/200'></div>
    </div>