Luka Reeson
Luka Reeson

Reputation: 123

Round Progress Bar - CSS/JS

I have this round progress bar that shows the progress of completed tasks. On progress change, the bar increases/decreases over 0.5s. The bar itself is made out of 2 halves, so in my JS I had to add some extra functions to delay and change the transition time if, for example, you would have an uneven number of total tasks.

Now, this all works just fine, until I "reconstruct" the progress bar (clicking the "reload" button) by just emptying the bar wrapper and appending all the divs and necessary data to re-display the bar with its current progress. At this point, whenever the progress bar hits the midpoint, for some reason, the transition isn't smooth as prior to "reload", but if you refresh the page again, the transition (at midpoint) is all fine. I have no clue why this happens...

Here's the JSFiddle

UPDATE In the "on reload" in JS, I forgot to re-initialize "bar_transition = $('.circle .bar_transition .sub-progress')"

function updateSubtaskProgressBar(subs_completed, total_subs, progress_bar_transition, toggle, mod, reload_btn) {
  var left_side = $(".sub-progress-bar_transition .circle .left .sub-progress");
  var right_side = $(".sub-progress-bar_transition .circle .right .sub-progress");

  progress = subs_completed / total_subs * 360;
  transition = 500;
  delay = transition / 2;
  rot_reminder = 0;

  if (progress < 180) {
    
    rot_right = 0;

    right_side.css({
      'transform': 'rotate(' + rot_right + 'deg)'
    });

    rot_left = progress

    rot_reminder = 180 - rot_left
  
    if (rot_reminder != 0 && mod != 0 && toggle == 0) {

      progress_bar_transition.css({
        'transition': 'all ' + delay / 1000 + 's ease-in'
      });

      setTimeout(function () {
        progress_bar_transition.css({
          'transition': 'all ' + delay / 1000 + 's ease-out'
        });
        left_side.css({

          'transform': 'rotate(' + rot_left + 'deg)'
        });
      }, delay);

      toggle = 1 - toggle
      reload_btn.data('toggle', toggle)

    } else {
      progress_bar_transition.css({
        'transition': 'all ' + transition / 1000 + 's ease-in-out'
      });
      left_side.css({
        'transform': 'rotate(' + rot_left + 'deg)'
      });

    }

  } else {

    rot_left = 180;

    left_side.css({
      'transform': 'rotate(' + rot_left + 'deg)'
    });

    rot_right = progress - 180;

    rot_reminder = rot_right

    if (rot_reminder != 0 && mod != 0 && toggle == 1) {
      
      progress_bar_transition.css({
        'transition': 'all ' + delay / 1000 + 's ease-in'
      });

      setTimeout(function () {
        progress_bar_transition.css({
          'transition': 'all ' + delay / 1000 + 's ease-out'
        });
        right_side.css({

          'transform': 'rotate(' + rot_right + 'deg)'
        });
      }, delay);

      toggle = 1 - toggle
      reload_btn.data('toggle', toggle)

    } else {
      progress_bar_transition.css({
        'transition': 'all ' + transition / 1000 + 's ease-in-out'
      });
      right_side.css({
        'transform': 'rotate(' + rot_right + 'deg)'
      });
    }
  }
}

function setSubtaskProgressBar(subs_completed, total_subs, bar_transition) {

  modulo = 0;
  current_progress = 0;
  
  var left_side = $(".sub-progress-bar_transition .circle .left .sub-progress");
  var right_side = $(".sub-progress-bar_transition .circle .right .sub-progress");

  modulo = total_subs%2
  current_progress = subs_completed / total_subs * 360;

  var reload = $('.reload')
  reload.data('completed', subs_completed)
  reload.data('total', total_subs)
  reload.data('modulo', modulo)


  if (current_progress < 180) {

    rot_left = current_progress
    rot_right = 0

    left_side.css({
      'transform': 'rotate(' + rot_left + 'deg)'
    });

    reload.data('toggle', 1)

  } else {

    rot_left = 180
    rot_right = current_progress - 180

    left_side.css({
      'transform': 'rotate(' + rot_left + 'deg)'
    });

    right_side.css({
      'transform': 'rotate(' + rot_right + 'deg)'
    });

    reload.data('toggle', 0)

  }

  bar_transition.css({
    'transition': 'none'
  });
}


$(document).ready(function () {

  subs_completed = 3;
  total_subs = 5;

  bar_transition = $('.circle .bar_transition .sub-progress')
  
  number = $('.number')
  number.html(subs_completed + '/' + total_subs)

  setSubtaskProgressBar(subs_completed, total_subs, bar_transition)


  $(document).on('click', '.add', function () {
    subs_completed += 1
    $('.number').html(subs_completed + '/' + total_subs)

    reload = $('.reload')
    reload.data('completed', subs_completed)
    modulo = reload.data('modulo')
    toggle = reload.data('toggle')
    total = reload.data('total')

    updateSubtaskProgressBar(subs_completed, total, bar_transition, toggle, modulo, reload)
  })

  $(document).on('click', '.remove', function () {
    subs_completed -= 1;
    $('.number').html(subs_completed + '/' + total_subs)

    reload = $('.reload')
    reload.data('completed', subs_completed)
    modulo = reload.data('modulo')
    toggle = reload.data('toggle')
    total = reload.data('total')

    updateSubtaskProgressBar(subs_completed, total, bar_transition, toggle, modulo, reload)
  })


  $(document).on('click', '.reload', function () {

    num = $(this).data('completed') + '/' + $(this).data('total')

    data = '<div class="subtask-circular-progress sub-progress-bar_transition" data-completed="'+$(this).data('completed')+'" data-total="'+$(this).data('total')+'" data-modulo="'+$(this).data('modulo')+'" data-toggle="'+$(this).data('toggle')+'"><div class="inner"></div><div class="number">'+num+'</div><div class="circle"><div class="bar_transition left"><div class="sub-progress"></div></div><div class="bar_transition right"><div class="sub-progress"></div></div></div></div>'

    wrapper = $('.wrapper').empty();
    wrapper.append(data)


    setSubtaskProgressBar(subs_completed, total_subs, bar_transition) 

    console.log('reload')

  })

});
body{
    background-color: #333;
}

.subtask-circular-progress {
    position: absolute;
    left: 50%;
    top: 40%;
    height: 100px;
    width: 100px;
    background-color: red;
}

.subtask-circular-progress .inner {
    position: absolute;
    z-index: 6;
    top: 50%;
    left: 50%;
    height: 80px;
    width: 80px;
    margin: -40px 0 0 -40px;
    border-radius: 100%;
    background: #454545;
}

.subtask-circular-progress .number {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 10;
    font-size: 15px;
    font-weight: 500;
    color: white;
}

.subtask-circular-progress .bar_transition {
    position: absolute;
    height: 100%;
    width: 100%;
    background: #fff;
    -webkit-border-radius: 100%;
    border-radius: 100%;
    clip: rect(0px, 100px, 100px, 50px);
}

.circle .bar_transition .sub-progress {
    position: absolute;
    height: 100%;
    width: 100%;
    -webkit-border-radius: 100%;
    border-radius: 100%;
    clip: rect(0px, 50px, 100px, 0px);
    background: #4158d0;
    transition: all 0.5s ease-in-out;
}

.circle .left .sub-progress {
    transform: rotate(0deg);
}


.circle .right {
    transform: rotate(180deg);
    z-index: 3;

}

.circle .right .sub-progress {
    transform: rotate(0deg);
}

.buttons{
    position: absolute;
    left: 50%;
    top: 60%;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
  <div class="subtask-circular-progress sub-progress-bar_transition">
    <div class="inner"></div>
    <div class="number">100%</div>
    <div class="circle">
      <div class="bar_transition left">
        <div class="sub-progress"></div>
      </div>
      <div class="bar_transition right">
        <div class="sub-progress"></div>
      </div>
    </div>
  </div>
</div>
<div class="d-flex buttons">
  <button type="button" class="btn btn-success add">+</button>
  <button type="button" class="btn btn-success remove ms-4">-</button>
  <button type="button" class="btn btn-success reload ms-4" data-completed="" data-total="" data-modulo="" data-toggle="">reload</button>
</div>

Upvotes: 1

Views: 805

Answers (1)

Greedo
Greedo

Reputation: 3559

The issue was tricky to find, but you are referring to the bar_transition global variable after that the relative content of the DOM is deleted.

After the DOM replacement, the content of bar_transition does not reflect the DOM anymore, is lost.

You need to manually update the content of the variable is the reload callback:

bar_transition = $('.circle .bar_transition .sub-progress');

function updateSubtaskProgressBar(subs_completed, total_subs, progress_bar_transition, toggle, mod, reload_btn) {
  var left_side = $(".sub-progress-bar_transition .circle .left .sub-progress");
  var right_side = $(".sub-progress-bar_transition .circle .right .sub-progress");

  progress = subs_completed / total_subs * 360;
  transition = 500;
  delay = transition / 2;
  rot_reminder = 0;

  if (progress < 180) {
    
    rot_right = 0;

    right_side.css({
      'transform': 'rotate(' + rot_right + 'deg)'
    });

    rot_left = progress

    rot_reminder = 180 - rot_left
  
    if (rot_reminder != 0 && mod != 0 && toggle == 0) {

      progress_bar_transition.css({
        'transition': 'all ' + delay / 1000 + 's ease-in'
      });

      setTimeout(function () {
        progress_bar_transition.css({
          'transition': 'all ' + delay / 1000 + 's ease-out'
        });
        left_side.css({

          'transform': 'rotate(' + rot_left + 'deg)'
        });
      }, delay);

      toggle = 1 - toggle
      reload_btn.data('toggle', toggle)

    } else {
      progress_bar_transition.css({
        'transition': 'all ' + transition / 1000 + 's ease-in-out'
      });
      left_side.css({
        'transform': 'rotate(' + rot_left + 'deg)'
      });

    }

  } else {

    rot_left = 180;

    left_side.css({
      'transform': 'rotate(' + rot_left + 'deg)'
    });

    rot_right = progress - 180;

    rot_reminder = rot_right

    if (rot_reminder != 0 && mod != 0 && toggle == 1) {
      
      progress_bar_transition.css({
        'transition': 'all ' + delay / 1000 + 's ease-in'
      });

      setTimeout(function () {
        progress_bar_transition.css({
          'transition': 'all ' + delay / 1000 + 's ease-out'
        });
        right_side.css({

          'transform': 'rotate(' + rot_right + 'deg)'
        });
      }, delay);

      toggle = 1 - toggle
      reload_btn.data('toggle', toggle)

    } else {
      progress_bar_transition.css({
        'transition': 'all ' + transition / 1000 + 's ease-in-out'
      });
      right_side.css({
        'transform': 'rotate(' + rot_right + 'deg)'
      });
    }
  }
}

function setSubtaskProgressBar(subs_completed, total_subs, bar_transition) {

  modulo = 0;
  current_progress = 0;
  
  var left_side = $(".sub-progress-bar_transition .circle .left .sub-progress");
  var right_side = $(".sub-progress-bar_transition .circle .right .sub-progress");

  modulo = total_subs%2
  current_progress = subs_completed / total_subs * 360;

  var reload = $('.reload')
  reload.data('completed', subs_completed)
  reload.data('total', total_subs)
  reload.data('modulo', modulo)


  if (current_progress < 180) {

    rot_left = current_progress
    rot_right = 0

    left_side.css({
      'transform': 'rotate(' + rot_left + 'deg)'
    });

    reload.data('toggle', 1)

  } else {

    rot_left = 180
    rot_right = current_progress - 180

    left_side.css({
      'transform': 'rotate(' + rot_left + 'deg)'
    });

    right_side.css({
      'transform': 'rotate(' + rot_right + 'deg)'
    });

    reload.data('toggle', 0)

  }

  bar_transition.css({
    'transition': 'none'
  });
}


$(document).ready(function () {

  subs_completed = 3;
  total_subs = 5;

  bar_transition = $('.circle .bar_transition .sub-progress')
  
  number = $('.number')
  number.html(subs_completed + '/' + total_subs)

  setSubtaskProgressBar(subs_completed, total_subs, bar_transition)


  $(document).on('click', '.add', function () {
    subs_completed += 1
    $('.number').html(subs_completed + '/' + total_subs)

    reload = $('.reload')
    reload.data('completed', subs_completed)
    modulo = reload.data('modulo')
    toggle = reload.data('toggle')
    total = reload.data('total')

    updateSubtaskProgressBar(subs_completed, total, bar_transition, toggle, modulo, reload)
  })

  $(document).on('click', '.remove', function () {
    subs_completed -= 1;
    $('.number').html(subs_completed + '/' + total_subs)

    reload = $('.reload')
    reload.data('completed', subs_completed)
    modulo = reload.data('modulo')
    toggle = reload.data('toggle')
    total = reload.data('total')

    updateSubtaskProgressBar(subs_completed, total, bar_transition, toggle, modulo, reload)
  })


  $(document).on('click', '.reload', function () {

    num = $(this).data('completed') + '/' + $(this).data('total')

    data = '<div class="subtask-circular-progress sub-progress-bar_transition" data-completed="'+$(this).data('completed')+'" data-total="'+$(this).data('total')+'" data-modulo="'+$(this).data('modulo')+'" data-toggle="'+$(this).data('toggle')+'"><div class="inner"></div><div class="number">'+num+'</div><div class="circle"><div class="bar_transition left"><div class="sub-progress"></div></div><div class="bar_transition right"><div class="sub-progress"></div></div></div></div>'

    wrapper = $('.wrapper').empty();
    wrapper.append(data)
    
    // Fix is here!
    bar_transition = $('.circle .bar_transition .sub-progress');

    setSubtaskProgressBar(subs_completed, total_subs, bar_transition) 

    console.log('reload')

  })

});
body{
    background-color: #333;
}

.subtask-circular-progress {
    position: absolute;
    left: 50%;
    top: 40%;
    height: 100px;
    width: 100px;
    background-color: red;
}

.subtask-circular-progress .inner {
    position: absolute;
    z-index: 6;
    top: 50%;
    left: 50%;
    height: 80px;
    width: 80px;
    margin: -40px 0 0 -40px;
    border-radius: 100%;
    background: #454545;
}

.subtask-circular-progress .number {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 10;
    font-size: 15px;
    font-weight: 500;
    color: white;
}

.subtask-circular-progress .bar_transition {
    position: absolute;
    height: 100%;
    width: 100%;
    background: #fff;
    -webkit-border-radius: 100%;
    border-radius: 100%;
    clip: rect(0px, 100px, 100px, 50px);
}

.circle .bar_transition .sub-progress {
    position: absolute;
    height: 100%;
    width: 100%;
    -webkit-border-radius: 100%;
    border-radius: 100%;
    clip: rect(0px, 50px, 100px, 0px);
    background: #4158d0;
    transition: all 0.5s ease-in-out;
}

.circle .left .sub-progress {
    transform: rotate(0deg);
}


.circle .right {
    transform: rotate(180deg);
    z-index: 3;

}

.circle .right .sub-progress {
    transform: rotate(0deg);
}

.buttons{
    position: absolute;
    left: 50%;
    top: 60%;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
  <div class="subtask-circular-progress sub-progress-bar_transition">
    <div class="inner"></div>
    <div class="number">100%</div>
    <div class="circle">
      <div class="bar_transition left">
        <div class="sub-progress"></div>
      </div>
      <div class="bar_transition right">
        <div class="sub-progress"></div>
      </div>
    </div>
  </div>
</div>
<div class="d-flex buttons">
  <button type="button" class="btn btn-success add">+</button>
  <button type="button" class="btn btn-success remove ms-4">-</button>
  <button type="button" class="btn btn-success reload ms-4" data-completed="" data-total="" data-modulo="" data-toggle="">reload</button>
</div>

Upvotes: 1

Related Questions