4

I am trying to make a progress indicator div using CSS animations. Please refer to the linked JSBin. The behavior I am trying to achieve is, when the user clicks on stage3 after stage1, the div width should increase from current 30% to 100% and not start from 0% as it is doing right now. I know if i set the 0% value as width: 30%, I can get that. But it won't work so well if I have many stages. I want the animation to start from final width of last stage to the width specified in new stage.

Progress indicator snippet

document.getElementById('stage1').addEventListener('click', function() {
  document.getElementById('progress').classList.add('stage1');
}, false);

document.getElementById('stage2').addEventListener('click', function() {
  document.getElementById('progress').classList.add('stage2');
}, false);

document.getElementById('stage3').addEventListener('click', function() {
  document.getElementById('progress').classList.add('stage3');
}, false);
.progress-wrapper {
  height: 10px;
  width: 400px;
  background-color: #BBDEFB;
}
#progress {
  background-color: #AADE00;
  height: 10px;
  width: 0;
}
.stage1 {
  animation-name: stage1;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}
@keyframes stage1 {
  to {
    width: 30%;
  }
}
.stage2 {
  animation-name: stage2;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}
@keyframes stage2 {
  to {
    width: 70%;
  }
}
.stage3 {
  animation-name: stage3;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}
@keyframes stage3 {
  to {
    width: 100%;
  }
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>

<body>
  <div class="progress-wrapper">
    <div id="progress"></div>
  </div>
  <pre>
    
  </pre>
  <button id="stage1">stage1</button>
  <button id="stage2">stage2</button>
  <button id="stage3">stage3</button>
</body>

</html>
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
nandwana92
  • 105
  • 2
  • 11
  • When there are multiple animations involved using same properties, CSS will use the latest and override the last. This is almost similar to this behavior: http://stackoverflow.com/questions/32224802/extend-the-final-state-of-the-first-animation-for-translated-element – m4n0 Nov 15 '15 at 17:21
  • @ManojKumar Thank you. I will have to think of a different approach, there are just way too many combinations of starting value and final value for width. – nandwana92 Nov 15 '15 at 17:42
  • Yes, I would suggest you to use Greensock Tweenmax/Timelinemax library. It is easy and fits your requirement. – m4n0 Nov 15 '15 at 17:44

2 Answers2

3

I'm not aware of a CSS-only solution, but you could set the width of the progress element before adding the class.

In doing so, it will transition from the previous width rather than from 0.

var progress = document.getElementById('progress');

document.getElementById('stage1').addEventListener('click', function() {
  setPreviousWidth();
  progress.classList.add('stage1');
}, false);

document.getElementById('stage2').addEventListener('click', function() {
  setPreviousWidth();
  progress.classList.add('stage2');
}, false);

document.getElementById('stage3').addEventListener('click', function() {
  setPreviousWidth();
  progress.classList.add('stage3');
}, false);

function setPreviousWidth () {
  progress.style.width = progress.offsetWidth + 'px';
}
.progress-wrapper {
  height: 10px;
  width: 400px;
  background-color: #BBDEFB;
}
#progress {
  background-color: #AADE00;
  height: 10px;
  width: 0;
}
.stage1 {
  animation-name: stage1;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}
@keyframes stage1 {
  to {
    width: 30%;
  }
}
.stage2 {
  animation-name: stage2;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}
@keyframes stage2 {
  to {
    width: 70%;
  }
}
.stage3 {
  animation-name: stage3;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}
@keyframes stage3 {
  to {
    width: 100%;
  }
}
<div class="progress-wrapper">
  <div id="progress"></div>
</div>
<pre>
    
  </pre>
<button id="stage1">stage1</button>
<button id="stage2">stage2</button>
<button id="stage3">stage3</button>
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
0

you can use custom CSS variable:

  1. create custom variable in css e.g

element{ --temp_variable: 0; }
  1. get current property value in JavaScript and assign to custom variable e.g. if you need color value of element <p> :

element = querySelector("p");  //or any other selector
computed_element = getComputedStyle(element);
current_color = computed_element.getPropertyValue("color")
computed_element.setProperty("--temp_variable",current_color);
  1. use that variable in CSS keyframe e.g.

@keyframe{
0%{ color: var(--temp_variable) }
100% { ..... }
}
Gull Noor
  • 1
  • 2