Animate transform only one property (scale) overri

2019-01-24 17:11发布

问题:

The problem is that the transform property's value has multiple part like translate, scale etc.

This is a theoretical question about element, let's .loader that has transform:translate(10px, 10px) and in the animation I want to animate the scale property. In this case, the browser will not take the transform:translate(10px, 10px) and will take only the scale.

I am looking for a way around this problem.

Here is an example to this question. Please, keep attention that I'm not looking for an answer to this particular example (like: wrap the element or add the translate value to the animation definition) but a generic solution (if exist, of course).

.loading {
  position: relative;
  width: 100px;
  height: 100px;
  background: #eee;
}
.loading:before,
.loading:after {
  content: "";
  width: 50%;
  height: 50%;
  -moz-border-radius: 50%;
  -webkit-border-radius: 50%;
  border-radius: 50%;
  background-color: #fff;
  opacity: 0.6;
  position: absolute;
  top: 0;
  left: 0;
  /* the broswer doesn't take this */
  transform: translate(100px, 300px);
  -webkit-animation: bounce 2s infinite ease-in-out;
  animation: bounce 2s infinite ease-in-out;
}
.loading:after {
  -webkit-animation-delay: -1s;
  animation-delay: -1s;
}
@keyframes bounce {
  0%, 100% {
    transform: scale(0);
    -webkit-transform: scale(0);
  }
  50% {
    transform: scale(1);
    -webkit-transform: scale(1);
  }
}
<div class="loading"></div>

回答1:

Generally when you add an animation with changes to the transform property then the transforms that are specified in the base element should also be carried over to be present within the animation's keyframes also. That is, the new transforms (that are part of the animation) should be added over on top of the existing transform and not overwrite it. Below is how it should be done.

.loading {
  position: relative;
  width: 200px;
  height: 200px;
  background: #eee;
}
.loading:before,
.loading:after {
  content: "";
  width: 50%;
  height: 50%;
  border-radius: 50%;
  background-color: #fff;
  opacity: 0.6;
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(100px, 300px);
  animation: bounce 2s infinite ease-in-out;
}
.loading:after {
  animation-delay: -1s;
}
@keyframes bounce {
  0%, 100% {
    transform: scale(0) translate(100px, 300px);
  }
  50% {
    transform: scale(1) translate(100px, 300px);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="loading"></div>

I wrote a similar answer here to a question about adding multiple animations on an element with each of those animations modifying the transform property's values independent of the other. I am linking it here only for reference and don't think they are duplicates.


Having said the above, adding the the original transform to each animation's kefyrames is not possible when you are trying to create animation libraries or trying to split each animation into a separate class. Say for example, you want to add the same bounce animation to multiple elements and each of them have a different initial transform setting then it becomes impossible to add it to animation's keyframe.

In such cases, you can still achieve the desired output using CSS but it would be very difficult (almost impossible in my opinion) to get it done with a single element.

What options do you have? Well, one option is for you to add the animation on a wrapper element.

.loading-wrapper {
  position: relative;
  width: 200px;
  height: 200px;
  background: #eee;
}
.loading-before, .loading-after {
  position: absolute;
  width: 50%;
  height: 50%;
  top: 0px;
  left: 0px;
  animation: bounce 2s infinite ease-in-out;
}
.loading-before:before,.loading-after:before {
  content: "";
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-color: #fff;
  opacity: 0.6;
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(100px, 300px);
}
.loading-after {
  animation-delay: -1s;
}
@keyframes bounce {
  0%, 100% {
    transform: scale(0);
  }
  50% {
    transform: scale(1);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="loading-wrapper">
  <div class="loading-before"></div>
  <div class="loading-after"></div>
</div>


The solution is quite generic and you can apply it to almost all such cases. The drawback is that if you want to stack multiple such transformations then you'd likely end up with multiple such wrappers. There is no pure CSS way other than adding original transformations within the animation's keyframes also.

The below snippet is another sample.

.move-n-scale {
  position: relative;
  height: 100px;
  width: 100px;
  background: sandybrown;
  border: 1px solid chocolate;
  transform: scale(0.5);
  animation: move 1s linear infinite alternate-reverse;
}
.move {
  position: relative;
  display: inline-block;
  animation: move-only 1s linear infinite alternate-reverse;
}
.scale {
  position: absolute;
  height: 100px;
  width: 100px;
  top: 0px;
  left: 0px;
  background: sandybrown;
  border: 1px solid chocolate;
  transform: scale(0.5);
}
@keyframes move {
  from {
    transform: translateX(0px) scale(0.5);
  }
  to {
    transform: translateX(300px) scale(0.5);
  }
}
@keyframes move-only {
  from {
    transform: translateX(0px);
  }
  to {
    transform: translateX(300px);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='move-n-scale'></div>
<div class='move'>
  <div class='scale'></div>
</div>

Note: Just to clarify, I did notice that you had mentioned about not wanting a solution which is very specific to this problem like wrap it etc. But, I had still added this solution as an answer because it is the only generic solution which I am aware of. I had added the second snippet only to show that is is indeed generic.



回答2:

You can delete the translate(100px, 300px); in .loading:after, then set the translate(100px, 300px) in @keyframes, like follows:

@keyframes bounce {
  0%,
  100% {
    transform: scale(0)translate(100px, 300px);
    -webkit-transform: scale(0)translate(100px, 300px);
  }
  50% {
    transform: scale(1)translate(100px, 300px);
    ;
    -webkit-transform: scale(1)translate(100px, 300px);
  }
}