2

https://jsfiddle.net/elgs/580nhepk/11/

This tiny demo is trying to slide up/down a div with the ease-in timing/easing function. However, it seems the easing effect is only effective the first time it slides down. From the second time onward, the easing effect seems to be disappeared.

HTML

<button id='slideup'>Slide Up</button>
<button id='slidedown'>Slide Down</button>
<div id="a"></div>

Javascript

const a = document.querySelector('div#a');
const u = document.querySelector('button#slideup');
const d = document.querySelector('button#slidedown');

u.addEventListener('click', (e) => {
  a.style.setProperty('--height', '30px');
  a.classList.add('slide');
});

d.addEventListener('click', (e) => {
  a.style.setProperty('--height', '100px');
  a.classList.add('slide');
});

CSS

div#a {
  width: 100px;
  height: 100px;
  box-sizing: border-box;
  border: 1px solid gray;
}

@keyframes slide {
  to {
    height: var(--height);
  }
}

.slide {
  animation-name: slide;
  animation-duration: .4s;
  animation-timing-function: ease-in;
  animation-delay: 0s;
  animation-direction: normal;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-play-state: running;
}

2 Answers2

2

This is because you've set your animation to run only once, so it won't run again after, unless you remove the animated class from the element every time + trigger a reflow + update its height property.

const a = document.querySelector('div#a');
const u = document.querySelector('button#slideup');
const d = document.querySelector('button#slidedown');

u.addEventListener('click', (e) => {
  a.style.setProperty('height', '100px'); // so the animation knows the 'from' value
  a.style.setProperty('--height', '30px');
  a.classList.remove('slide'); // so the animation starts again
  a.offsetHeight; // so the no-class be calculated
  a.classList.add('slide');
});

d.addEventListener('click', (e) => {
  a.style.setProperty('height', '30px');
  a.style.setProperty('--height', '100px');
  a.classList.remove('slide');
  a.offsetHeight;
  a.classList.add('slide');
});
div#a {
  width: 100px;
  height: 100px;
  box-sizing: border-box;
  border: 1px solid gray;
}

@keyframes slide {
  to {
    height: var(--height);
  }
}

.slide {
  animation-name: slide;
  animation-duration: .4s;
  animation-timing-function: ease-in;
  animation-delay: 0s;
  animation-direction: normal;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-play-state: running;
}
<button id='slideup'>Slide Up</button>
<button id='slidedown'>Slide Down</button>
<div id="a"></div>

But anyway, what you want is not an animation but a transition:

const a = document.querySelector('div#a');
const u = document.querySelector('button#slideup');
const d = document.querySelector('button#slidedown');

u.addEventListener('click', (e) => {
  a.style.setProperty('--height', '30px');
});

d.addEventListener('click', (e) => {
  a.style.setProperty('--height', '100px');
});
div#a {
  width: 100px;
  height: 100px;
  height: var(--height, 100px);
  box-sizing: border-box;
  border: 1px solid gray;
}

.slide {
  transition: height .4s ease-in;
}
<button id='slideup'>Slide Up</button>
<button id='slidedown'>Slide Down</button>
<div id="a" class="slide"></div>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Thanks. But it seems even if each time I remove the animated class, it's still the same. https://jsfiddle.net/elgs/580nhepk/29/ –  Apr 11 '18 at 07:16
  • @ElgsQianChen I guess you didn't shared the correct fiddle. Also, it's "*remove the animated class from the element every time **+** trigger a reflow **+** update its height property*" => not something you want to do. – Kaiido Apr 11 '18 at 07:29
  • 1
    @ElgsQianChen so you are indeed missing the "***+** trigger a reflow **+** update its height property*" parts. Look at the first snippet in the answer, it's already there. But once again, you probably don't want to do that. Just use a transition. – Kaiido Apr 11 '18 at 07:33
  • How can I add trigger a reflow? I'm sorry I haven't caught it in the answer. And I started to be confused about animation vs. transition. –  Apr 11 '18 at 07:36
  • 1
    https://gist.github.com/paulirish/5d52fb081b3570c81e3a In the answer `a.offsetHeight` does that. – Kaiido Apr 11 '18 at 07:40
  • I got transition to work. But transition has an undesired side effect. That is it forces future changes of `height` css property to apply the same transition rules. –  Apr 11 '18 at 08:08
  • @ElgsQianChen remove the slide class when you don't want it to transition. – Kaiido Apr 11 '18 at 08:09
  • Upon removing the slide class, the div lost its transitioned css properties, which I still want to keep. Should I manually apply all css rules on the transition end call back again? –  Apr 11 '18 at 08:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/168708/discussion-between-kaiido-and-elgs-qian-chen). – Kaiido Apr 11 '18 at 08:12
0

css animation usully execute animation at the first time when the page has loaded. In this situation,you should use css transition instead of css animation for Example:

.slide-up {
height : 30px;
}
.slide-down {
height : 60px;
}
#a{
transition: height 200ms
}
zero
  • 51
  • 1
  • 4