Search code examples
htmlcssabsolute

Expanding absolute div from center to reveal child


I want to have a(n absolutely-positioned) rectangular mask that expands on hover to reveal its (variable height) children. Right now the way that I'm achieving this is with an absolutely positioned div that changes its left, width and max-height values. However, I'd like to make it look like it's expanding from the middle instead of revealing from the top left--and all at the same time.

<div id="container">
 <div id="mask">
  <div id="background"></div>
 </div>
</div>

    #container {
     width: 300px;
     height: 300px;
     background-color: black;
    }

    #mask {
     width: 50px;
     max-height: 50px;
     position: absolute;
     top: 125px;
     left: 125px;
     overflow: hidden;

     transition: width 1s, max-height 1s, left 1s, top 1s;
    }

    #background {
     background-image: url(https://www.pngfind.com/pngs/m/299-2991041_memes-para-stickers-png-png-download-surprised-pikachu.png);
     background-size: contain;
     width: 150px;
     height: 125px;
    }

    #mask:hover {
     width: 150px;
     max-height: 1000px;
     left: 75px;
     top: 75px;
    }

Here's a codepen: https://codepen.io/jraynolds/pen/OJJxpOm


Solution

  • #1 – Using clip-path

    Compatibility: All modern browsers apart from Edge. IE10/11 and Edge provide limited support using url() only.

    Example with clip-path

    To crop the image, use clip-path: inset(). In this example, we have a 120 pixel box that is reduced to 0 on hover.

    .reveal {
      background: url(https://i.sstatic.net/3v1Kz.jpg) no-repeat;
      background-size: 150px;
      background-position: center;
      height: 300px;
      width: 300px;
      clip-path: inset(120px 120px 120px 120px);
      transition: clip-path 0.5s;
    }
    
    .reveal:hover {
      clip-path: inset(0 0 0 0);
    }
    
    /*for example*/
    body {
      background: linear-gradient(to bottom right, black 0%, white 100%);
      height: 100vh;
    }
    <div class="reveal"></div>

    Example with url() (not working in Edge or IE)

    There was an attempt!

    Create an SVG like so:

    <svg>
        <clipPath id="square">
             <rect />
        </clipPath>
    </svg>
    

    and place that in the container. The div is given clip-path: url(#square) and the width, height, x and y coordinates are provided in the CSS and changed on hover.

    .reveal-url {
      background: url(https://www.pngfind.com/pngs/m/299-2991041_memes-para-stickers-png-png-download-surprised-pikachu.png)
        no-repeat;
      background-size: 150px;
      background-position: center;
      height: 300px;
      width: 300px;
      clip-path: url(#square);
      position: relative;
    }
    
    svg {
      width: 100%;
      height: 100%;
    }
    
    .reveal-url rect {
      transition: all 0.5s;
      x: 120px;
      y: 120px;
      width: 60px;
      height: 60px;
    }
    
    
    .reveal-url:hover rect {
      width: 100%; 
      height: 100%;
      x: 0;
      y: 0;
    }
    
    /*for example*/
    body {
      background: linear-gradient(to bottom right, black 0%, white 100%);
      height: 100vh;
    }
    
    h1 {
      font-size: 30px;
      color: white;
    }
    <h1>This only works in Chrome and Firefox.</h1>
    
    <div class="reveal-url">
      <svg>
          <clipPath id="square">
    			  <rect />
          </clipPath>
      </svg>
    </div>

    #2 – Using Box-shadow

    If you are working with solid background colours, a simple method is to use inset box-shadow to mask the contents of the container and then reduce the box shadow on hover.

    .reveal {
      background: #000 url(https://www.pngfind.com/pngs/m/299-2991041_memes-para-stickers-png-png-download-surprised-pikachu.png) no-repeat;
      background-size: 150px;
      background-position: center;
      height: 300px;
      width: 300px;
      box-shadow: inset 0 0 0 120px #000;
      transition: box-shadow 1s;
    }
    
    .reveal:hover {
      box-shadow: inset 0 0 0 70px #000;
    }
    
    
    /*for example*/
    
    body {
      background: #000;
    }
    <div class="reveal"></div>