How can I transform a line into two and rotate with css

Viewed 2318

I want to have a straight line in my header, and then a few seconds after the page loads, I want those lines to slowly move down until they look like the one in the image below:

enter image description here

I thought of using css transform property to rotate two rotate two divs, but that does not seem to be a solution as you can see the result in my pen here

HTML:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<div id="big">
  <div class="arrow-box">
    <div class="line line-1"></div>    
    <div class="line line-2"></div>

  </div>
</div>

CSS:

#big{
  background: red;
  height: 200px;
}
.arrow-box{
  max-width: 200px;
  margin: 0 auto;
  padding-top: 10px;

}
.line{
  background: white;
  width: 60px;
  height: 1px;
}

.line-1{
  transform: rotate(20deg);
}

.line-2{
  transform: rotate(-20deg);
}

How can I make a/the line look like the icon on the image after the page loads?

2 Answers

You could do this using css animation. You could use rotateZ transform to create arrow shape and also scale to keep increasing width of the lines as animation goes.

You also need to use transform-origin for both parts to transform at the right point.

.line {
  position: relative;
  width: 100px;
}

.line:after,
.line:before {
  background: black;
  position: absolute;
  content: "";
  height: 2px;
  width: 50%;
  bottom: 0;
}

.line:before {
  left: 0;
  animation: moveBefore 1s linear forwards;
  transform-origin: center left;
}

.line:after {
  right: 0;
  animation: moveAfter 1s linear forwards;
  transform-origin: center right;
}

@keyframes moveBefore {
  0% {
    transform: rotateZ(0) scale(1, 1);
  }
  50% {
    transform: rotateZ(15deg) scale(1.05, 1);
  }
  
  100% {
    transform: rotateZ(30deg) scale(1.16, 1);
  }
}

@keyframes moveAfter {
  0% {
    transform: rotateZ(0) scale(1, 1);
  }
  50% {
    transform: rotateZ(-15deg) scale(1.05, 1);
  }
  
  100% {
    transform: rotateZ(-30deg) scale(1.16, 1);
  }
}
<div class="line"></div>

You could also do this with svg using line element and some javascript to move y position left and right line parts. To increase angle gradually you can use setInterval method.

let step = 0;
const left = document.querySelector('.left-line');
const right = document.querySelector('.right-line');

function move(el, prop, size) {
  el.setAttribute(prop, +el.getAttribute(prop) + size);
}

setInterval(() => {
  if (step <= 40) {
    move(left, 'y2', 0.8);
    move(right, 'y1', 0.8)
    step += 1;
  }
}, 30)
<svg xmlns="http://www.w3.org/2000/svg">
  <line class="left-line" x1="0" y1="20" x2="40" y2="20" stroke="black" />
  <line class="right-line" x1="40" y1="20" x2="80" y2="20" stroke="black" />
</svg>

While the accepted answer works just fine the artist in me can't take the overlapping of the lines in the center due to the scaling. Here's a few alternate options:

Option 1 - clip-path

Using clip-path, animate the mid-points of a rectangle to transform the polygon into a chevron. This works by masking the background color of the element outside of the animated shape.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.line {
  display: inline-block;
  width: 200px;
  height: 50px;
  background-color: black;
  clip-path: polygon(0 0, 100% 0, 100% 2px, 0 2px);
  animation: 2s infinite linear;
}

.line.down {
  animation-name: chevron-down;
}

.line.up {
  animation-name: chevron-up;
}

@keyframes chevron-down {
  from {
    clip-path: polygon(0 0, 50% 0, 100% 0, 100% 2px, 50% 2px, 0 2px);
  }
  to {
    clip-path: polygon(0 0, 50% 48px, 100% 0, 100% 2px, 50% 50px, 0 2px);
  }
}

@keyframes chevron-up {
  from {
    clip-path: polygon(0 48px, 50% 48px, 100% 48px, 100% 50px, 50% 50px, 0 50px);
  }
  to {
    clip-path: polygon(0 0, 50% 48px, 100% 0, 100% 2px, 50% 50px, 0 2px);
  }
}
<div class="line down"></div>
<div class="line up"></div>

Support for clip-path is spotty, however.


Option 2 - pseudo-elements

If you can't use clip-path or prefer to use pseudo elements, change their placement and origins of transform to come from the center (and not the upper corners):

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.line {
  position: relative;
  width: 200px;
  height: 50px;
  overflow: hidden;
}

.line::before,
.line::after {
  position: absolute;
  content: '';
  display: block;
  bottom: 0;
  height: 2px;
  width: 50%;
  background-color: black;
  animation: 2s linear infinite;
}

.line::before {
  transform-origin: bottom right;
  left: 0;
  animation-name: before;
}
.line::after {
  transform-origin: bottom left;
  right: 0;
  animation-name: after;
}

@keyframes before {
  to { transform: rotateZ(30deg); }
}

@keyframes after {
  to { transform: rotateZ(-30deg); }
}
<div class="line"></div>

Related