1

I have been playing around with looping of CSS transitions (following this article).

The transition itself could be divided into 4 (looped) phases:
shrinked2=>expanded=>expanded2=>shrinked
These states are represented by 4 classes:
shrinked2 - stateOne
expanded - stateTwo
expanded2 - stateThree
shrinked2 - stateFour

I am, initially, setting the width and height of my element, being transitioned, in percents via id. Then, to trigger transition, I am changing transform: scale via state classes, listed above.

Up to his point it basically works fine for me, here is demonstration on JSFiddle.

Now, as the next step I would like to center the transitioned element, vertically and horizontally, on the page (and keep it there). I am doing so by adding the following, to the element id:
top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%); as well as appending each transform:, in the state classes, with translateX(-50%) translateY(-50%). Now this way the scale is not applied to the element (i.e. size is not changed on transition) - only background property is affected, transition only happens once (i.e. it is not looped anymore), as if transitionend was never fired. Here is the result on JSFiddle. Changing of expected property name (in loopTransition function) to background, has not effect (JSFiddle).

I am totally aware that there are lots of other ways to center the element. What I would like to understand is:

  1. Is it possible to combine translateX/translateY and scale in the same CSS transition (if yes, what am I doing wrong?)
  2. If element centering with top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%); is not compatible with transitions in general or scale in particular, what is the recommended method to use?

var the_circle = document.getElementById('circle');
the_circle.addEventListener("transitionend", loopTransition);

function startTransition() {
    var the_circle = document.getElementById('circle');
    if (the_circle.className === 'stateOne paused') {
        the_circle.style.transitionDuration = 1;
        the_circle.className = 'stateTwo animated';
    } else {
        stopTransition();
    }
}

function stopTransition() {
    var the_circle = document.getElementById('circle');
    the_circle.style.transitionDuration = "0.5s"
    the_circle.className = "stateOne paused"
}

function loopTransition(e) {
    var the_circle = document.getElementById('circle');
    if (e.propertyName === "transform") {
        if (the_circle.className.indexOf('paused') !== -1) {
            stopTransition()
        } else {
            if (the_circle.className === "stateTwo animated") {
                the_circle.style.transitionDuration = "1s";
                the_circle.className = "stateThree animated";
            } else if (the_circle.className === "stateThree animated") {
                the_circle.style.transitionDuration = "1s";
                the_circle.className = "stateFour animated";
            } else if (the_circle.className === "stateFour animated") {
                the_circle.style.transitionDuration = "1s";
                the_circle.className = "stateOne animated";
            } else if (the_circle.className === "stateOne animated") {
                the_circle.style.transitionDuration = "1s";
                the_circle.className = "stateTwo animated";
            }
        }
    }
}
#circle {
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
    position: absolute;
    width: 10%;
    padding-top: 10%;
    border-radius: 50%;
    transition: all 1s;
}

.stateOne {
    background: #800080;
    transform: scale(1.0001, 1.0001) translateX(-50%) translateY(-50%);
}

.stateTwo {
    background: #ffe6ff;
    transform: scale(2, 2) translateX(-50%) translateY(-50%);
}

.stateThree {
    background: #ffe6ff;
    transform: scale(2.0001, 2.0001) translateX(-50%) translateY(-50%);
}

.stateFour {
    background: #800080;
    transform: scale(1, 1) translateX(-50%) translateY(-50%);
}
<div id='circle' class="stateOne paused" onclick=startTransition()></div>
  • 1
    Have you tried with [`animation` & `keyframes`](https://developer.mozilla.org/en-US/docs/Web/CSS/animation)? – Sven Oct 12 '17 at 08:44
  • 1
    @Svenskunganka Yes I have, (see: [another question](https://stackoverflow.com/q/46402020/8554766) and [JSFiddle](https://jsfiddle.net/mfo4qk9dv2ua5iksttbnqx64c/rpve2e9L/)), and it's working perfectly fine. Though, as I am doing this stuff for studying purposes, my goal is to make it work with `transition` now. –  Apr 14 '20 at 07:14

1 Answers1

2
  1. In CSS, #id selectors take precedence over .class selectors. Therefore, the transform declarations in your .state classes never get applied. The solution is to either:

    • increase the specificity of your rule, i.e. #circle.stateTwo,

    • add the !important flag to your declaration:

      transform: scale(2, 2) translate(-50%, -50%) !important;
      

      However this should be avoided and the first method used whenever possible.

  2. An easier method to center elements, that doesn't mix with your animation, is to use flexbox:

    .flex-container {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100vw;
      height: 100vh;
    }
    
    #circle {
      width: 10%;
      padding-top: 10%;
      border-radius: 50%;
      transition: all 1s;
    }
    
    .stateOne {
      background: #800080;
      transform: scale(1.0001, 1.0001);
    }
    
    .stateTwo {
      background: #ffe6ff;
      transform: scale(2, 2);
    }
    
    .stateThree {
      background: #ffe6ff;
      transform: scale(2.0001, 2.0001);
    }
    
    .stateFour {
      background: #800080;
      transform: scale(1, 1);
    }
    
tobiv
  • 821
  • 1
  • 8
  • 20
  • I assume that `left` and `right` positioning by percentage does not take into account the `scale` transform, yet translate does. I might test this later on... – tobiv Oct 13 '17 at 14:43
  • The first suggested method (using `#circle.stateTwo`) centers the element initially, though [fails (JSFiddle)](https://jsfiddle.net/mfo4qk9dv2ua5iksttbnqx64c/1cf98h76/3/) to keep it centered during transition, I'd appreciate and explanation on why this is happening. The second, method (using `flexbox`) [works (JSFiddle)](https://jsfiddle.net/mfo4qk9dv2ua5iksttbnqx64c/1cf98h76/4/) as intended, so I think I will stick with it. In any case, thanks for the answer! –  Apr 14 '20 at 07:17