Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep box-shadow direction consistent while rotating

When giving e.g. a <div> a box-shadow as well as rotating it will cause a rotation of the box-shadow direction - which is problematic when the box-shadow should create an illusion of lighting.

Example: https://jsfiddle.net/5h7z4swk/

div {
  width: 50px;
  height: 50px;
  margin: 20px;
  box-shadow: 10px 10px 10px #000;
  display: inline-block;
}
#box1 {
  background-color: #b00;
}
#box2 {
  background-color: #0b0;
  transform: rotate(30deg);
}
#box3 {
  background-color: #00b;
  transform: rotate(60deg);
}
#box4 {
  background-color: #b0b;
  transform: rotate(90deg);
}
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
#box6 {
  background-color: #0bb;
  animation-name: spin;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<div id="box4"></div>
<div id="box6"></div>

box-shadow rotating with div

The answer to this problem should look similar to this mock up:

Shdaow direction is respected during rotation

How can I rotate a <div> and still keep the box-shadow going the same direction?

The solution should be pure CSS...

Note: The animation in the CSS is for demonstration purposes. The use case will use JavaScript to set the rotation. But the JavaScript will know nothing about the box-shadow as it is in the responsibility of the design to define a (or many...) shadows. That's why it should be a pure CSS solution.

like image 579
Chris Avatar asked Jan 30 '16 13:01

Chris


People also ask

What is blur radius in box shadow?

The blur radius (required), if set to 0 the shadow will be sharp, the higher the number, the more blurred it will be, and the further out the shadow will extend. For instance a shadow with 5px of horizontal offset that also has a 5px blur radius will be 10px of total shadow.


2 Answers

Keeping direction of an offset box-shadow consistent during rotation is simple with CSS transforms.
This approach relies on the fact that the transform origin is moved with the transforms. This means that when several transforms are set on the same element, the coordinate system of each transform changes according to the previous ones.

In the following example, the blue element is a pseudo element and the shadow is the div element:

div {    width: 40px; height: 40px;    margin: 40px;    box-shadow: 0px 0px 10px 5px #000;    animation: spinShadow 2s infinite;    background-color: #000;  }  @keyframes spinShadow {    to { transform: rotate(360deg); }  }  div:before {    content: '';    position: absolute;    left:-5px; top:-5px;    width: 50px; height: 50px;    transform: rotate(0deg) translate(-10px, -10px) rotate(0deg);    animation:inherit;    animation-name: spinElt;    background-color: #0bb;  }  @keyframes spinElt {    to { transform: rotate(-360deg) translate(-10px, -10px) rotate(360deg); }  }
<div></div>

Explanation of the transition property on the pseudo element (See the following code snippet for an illustration of the steps):

transform: rotate(-360deg) translate(-10px, -10px) rotate(360deg) 
  1. rotate(-360deg) counters the rotation of the parent to make the pseudo element static.
  2. translate(-10px, -10px) the pseudo element is translated to make the shadow offset
  3. rotate(360deg) the pseudo element is rotated in the same direction as parent

div {    width: 40px; height: 40px;    margin: 40px;    box-shadow: 0px 0px 10px 5px #000;    animation: spinShadow 2s infinite;    background-color: #000;  }  @keyframes spinShadow {    to { transform: rotate(360deg); }  }  div:before {    content: '';    position: absolute;    left:-5px; top:-5px;    width: 50px; height: 50px;    animation:inherit;    background-color: #0bb;  }  #first:before{    transform: rotate(0deg);    animation-name: first;  }    @keyframes first {    to { transform: rotate(-360deg); }  }  #second:before{    transform: rotate(0deg) translate(-10px, -10px);    animation-name: second;  }    @keyframes second {    to { transform: rotate(-360deg) translate(-10px, -10px); }  }  #complete:before{    transform: rotate(0deg) translate(-10px, -10px) rotate(0deg);    animation-name: complete;  }    @keyframes complete {    to { transform: rotate(-360deg) translate(-10px, -10px) rotate(360deg); }  }
<ol>    <li>Counter rotate:<div id="first"></div></li>    <li>Translate :<div id="second"></div></li>    <li>Rotate:<div id="complete"></div></li>  <ol>
like image 132
web-tiki Avatar answered Nov 02 '22 09:11

web-tiki


You could as well integrate box-shadow direction inside animation frames:

div {    display: inline-block;    margin: 1em ;    height: 50px;    width: 50px;    box-shadow: 15px 15px 15px 5px gray;    animation: rte 5s infinite linear;  }    .red {    background: red  }    .green {    background: green;    animation-delay:2s;  }    .blue {    background: blue;    animation-delay:4s;  }    .bob {    background: #b0b;    animation-delay:6s;  }    .cyan {    background: cyan;    animation-delay:8s;  }    @keyframes rte {    25% {      box-shadow:  15px -15px 15px 5px gray;    }    50% {        box-shadow:  -15px -15px 15px 5px gray;    }    75% {      box-shadow:  -15px 15px 15px 5px gray;    }    100% {      transform: rotate(360deg);    }  }
<div class="red"></div>  <div class="green"></div>  <div class="blue"></div>  <div class="bob"></div>  <div class="cyan"></div>
like image 41
G-Cyrillus Avatar answered Nov 02 '22 08:11

G-Cyrillus