Search code examples
cssreactjscss-animationsstyled-componentsclip

How to clip a square so that I can draw chasing borders?


In my React.js application, I have a square shape where I want to draw 2 borders chasing each other over the border perimeter. Chasing borders are always opposite to each other and can never catch each other. In this way I want to create an effect that looks like in the image below with the neon yellow color. I have been trying to achieve this now for a while and I am quite close but I fail to make the chasing borders properly. They seems to break while moving around the square and at the end I achieve something like the other image below.

The current result I have achieved now.
Desired outcomeCurrent progress

Here is the that I have tried so far to solve my problem: const chasingBorders1 = keyframes`
  0% {
    clip-path: polygon(100% 0%, 100% 40%, 60% 0%, 100% 0%);
  }
  25% {
    clip-path: polygon(100% 60%, 100% 100%, 60% 60%, 100% 40%);
  }
  50% {
    clip-path: polygon(0% 100%, 40% 100%, 0% 60%, 40% 60%);
  }
  75% {
    clip-path: polygon(0% 0%, 0% 40%, 40% 0%, 40% 40%);
  }
  100% {
    clip-path: polygon(100% 0%, 100% 40%, 60% 0%, 100% 0%);
  }
`;

const chasingBorders2 = keyframes`
  0% {
    clip-path: polygon(0% 100%, 0% 60%, 40% 100%, 0% 100%);
  }
  25% {
    clip-path: polygon(0% 0%, 0% 40%, 40% 0%, 40% 40%);
  }
  50% {
    clip-path: polygon(100% 0%, 100% 40%, 60% 0%, 100% 0%);
  }
  75% {
    clip-path: polygon(100% 60%, 100% 100%, 60% 60%, 100% 40%);
  }
  100% {
    clip-path: polygon(0% 100%, 0% 60%, 40% 100%, 0% 100%);
  }
`;

const ModeBox = styled(motion.div)`
  position: absolute;
  top: 45px;
  left: 33.5%;
  background: #020202;
  border: solid 5px white;
  border-radius: 30px;
  width: 580px;
  height: 100px;
  z-index: 95;

  &::before, &::after {
    content: "";
    position: absolute;
    top: -5px;
    left: -5px;
    right: -5px;
    bottom: -5px;
    border: solid 8px orange;
    border-radius: inherit;
    z-index: -1;
    box-shadow: 0 0 10px #6F5824, 0 0 15px #6F5824, 0 0 20px #6F5824;
  }

  &::before {
    animation: ${chasingBorders1} 4s infinite;
  }

  &::after {
    animation: ${chasingBorders2} 4s infinite;
  }
`;

Solution

  • As an option, you can apply linear-gradient and rotate animation:

    body {
      background-color: #000;
    }
    
    a{
      --border-radius: 16px;
      --border-width: 2px;
      padding: var(--border-width);
      border-radius: var(--border-radius);
      position: relative;
      overflow: hidden;
      will-change: transform;
      background-color: rgba(255,255,255,.1);
      display: inline-flex;
      color: #fff;
      text-decoration: none;
    }
    
    
    a span{
      padding: 12px 32px;
      background-color: #000;
      border-radius: calc(var(--border-radius) - var(--border-width));
    }
    
    a:before,
    a:after {
      content: '';
      position: absolute;
      z-index: -1;
      width: 100%;
      aspect-ratio: 1;
      position: absolute;
      animation: rotate 4s linear infinite;
      inset: 50% auto auto 50%;
    }
    
    a:before {
      background: linear-gradient(to top, transparent 50%, orange);
      clip-path: polygon(0 0, 50% 0, 50% 50%, 0 50%);
    }
    
    a:after{
      clip-path: polygon(100% 100%,50% 100%,50% 50%, 100% 50%);
      background: linear-gradient(to bottom, transparent 50%, orange);
    }
    
    @keyframes rotate {
      0% {
        transform: translate(-50%,-50%) rotate(0);
      }
      100% {
        transform: translate(-50%,-50%) rotate(1turn);
      }
    }
    <a href="#"><span>Lorem ipsum.</span></a>