devamat
devamat

Reputation: 2513

Rotating an element also changes scaleX in css animation

I have five image elements each containing a feather picture. I want to add them to the DOM, where some are randomly flipped right or left, with a random rotation angle.

I want each feather to animate so that they align with the X axis (ie back to 0deg), while also maintaining the scaleX. In my code below, the realignment works but for the flipped feathers, they also twist back the original "un-flipped" appearance during the animation.

How can I prevent this from happening? I know I could achieve this by using a <div> as a wrapper around each feather, but is there a better solution?

Javascript

function feathersPuff() {

    for (let i = 0; i < 5; i++) {

        let feather = document.createElement("img");

        feather.src = "imgs/feather" + i + ".svg";

        document.body.appendChild(feather);

        let plusOrMinus = Math.random() < 0.5 ? -1 : 1;

        feather.style.transform = "scaleX(" + plusOrMinus + ") rotate(" + getRandomInt(50) * plusOrMinus + "deg)"

        feather.classList.add("feather");    
    }    
}

CSS

.feather {    
    display: block;
    height: 2%;
    position: absolute;

    animation: realign;
    animation-iteration-count: 1;
    animation-direction: linear;
    animation-timing-function: ease-in-out;
    animation-duration: 500ms;
    animation-delay: 0;
    animation-fill-mode: forwards;        
}

@keyframes realign {

    100% { transform: rotate(0deg) }
}

Upvotes: 1

Views: 236

Answers (1)

Dacre Denny
Dacre Denny

Reputation: 30390

If I understand your question correctly, then a solution might be to define two animations, where one has it's final transform pre-multiplied with a negative scaleX for the negated feather (to ensure that it doesn't produce the undesirable "flip"/twist during the animation):

/* 
Define flipped feather animation with positive scale (redundant)
*/
@keyframes regular {
  100% {
    transform: scaleX(1) rotate(0deg)
  }
}

/* 
Define flipped feather animation with negated scale. Assuming the 
starting transform has scaleX(-1), this animation will stop the 
twisting effect from happening 
*/
@keyframes flipped {
  100% {
    transform: scaleX(-1) rotate(0deg)
  }
}

With these two animations defined, you can also define corresponding modifier classes to select and apply these randomly to feather elements:

function getRandomInt() {
  return Math.random() * 180;
}

function feathersPuff() {

  for (let i = 0; i < 10; i++) {

    let feather = document.createElement("img");

    /* Placeholder image - replace this with your svg */
    feather.src = "https://pngriver.com/wp-content/uploads/2017/12/download-free-birds-feather-png-transparent-images-transparent-backgrounds-feather_PNG12958-300x160.png";

    document.body.appendChild(feather);

    /* Randomly orrientate feather */
    if (Math.random() < 0.5) {

      /* If regular orrientation, then apply regular modifier class which
      will use the "regular" animation (without scaling) */
      feather.classList.add("regular");
      feather.style.transform =
        "rotate(" + getRandomInt(50) + "deg)";
    } else {

      /* If flipped orrientation, then apply flipped modifier class which
      will apply the "flipped" animation (with negated scaling factored in) */
      feather.classList.add("flipped");
      feather.style.transform =
        "scaleX(-1) rotate(" + getRandomInt(50) + "deg)"
    }
    
    feather.classList.add("feather");
  }
}

feathersPuff();
/* Define flipped feather animation with positive scale (redundant)*/
@keyframes regular {
  100% {
    transform: scaleX(1) rotate(0deg)
  }
}

/* Define flipped feather animation with negated scale */
@keyframes flipped {
  100% {
    transform: scaleX(-1) rotate(0deg)
  }
}

/* Modifier class which animates feathers of the "regular orientation" */
.feather.regular {
  animation-name: regular;
}

/* Modifier class which animates feathers of the "flipped orientation" */
.feather.flipped {
  animation-name: flipped;
}

.feather {
  height: 30px;
  display: block;
  /* position: absolute; Removed this to prevent feathers overlapping to better
  demonstrate the techniques final result */
  animation-iteration-count: 1;
  animation-direction: linear;
  animation-timing-function: ease-in-out;
  animation-duration: 2500ms;
  animation-fill-mode: forwards;
}

Upvotes: 1

Related Questions