循环播放动画时,只运行最后一个循环

时间:2018-06-17 05:32:29

标签: javascript jquery loops jquery-animate progress-bar

这是my previous question的后续跟进。

我有一个progressbar.js圈子,可以滚动动画。如果只有一个圆圈,则按预期工作。

现在我想通过循环一个具有不同键值对的对象来创建许多这些动画圆圈。

例如:

  var divsValues = {
    'total-score-circle': 0.75, 
    'general-score-circle': 0.80, 
    'speed-score-circle': 0.85, 
    'privacy-score-circle': 0.90,
  };

对于每个键值对,键是div ID,值是告诉动画要走多远的数字。

以下是我尝试实现循环的代码,但问题是只有最后一个圆圈在滚动时动画。所有圆圈都显示在“动画前”状态,但滚动到底部时,只有最后一个圆实际上变为动画。

我需要每个圆圈在视口中设置动画。

//Loop through my divs and create animated circle for each one
function makeCircles() {
  var divsValues = {
    'total-score-circle': 0.75,
    'general-score-circle': 0.80,
    'speed-score-circle': 0.85,
    'privacy-score-circle': 0.90,
  };

  for (var i in divsValues) {
    if (divsValues.hasOwnProperty(i)) {
      bgCircles(i, divsValues[i]);
    }
  }
}
makeCircles();

// Check if element is scrolled into view
function isScrolledIntoView(elem) {
  var docViewTop = jQuery(window).scrollTop();
  var docViewBottom = docViewTop + jQuery(window).height();
  var elemTop = jQuery(elem).offset().top;
  var elemBottom = elemTop + jQuery(elem).height();

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}

//Circle design and animation
function bgCircles(divid, countvalue) {
  // Design the circle using progressbar.js
  bar = new ProgressBar.Circle(document.getElementById(divid), {
    color: '#ddd',
    // This has to be the same size as the maximum width to
    // prevent clipping
    strokeWidth: 4,
    trailWidth: 4,
    easing: 'easeInOut',
    duration: 1400,
    text: {
      autoStyleContainer: false
    },
    from: {
      color: '#ddd',
      width: 4
    },
    to: {
      color: '#888',
      width: 4
    },
    // Set default step function for all animate calls
    step: function(state, circle) {
      circle.path.setAttribute('stroke', state.color);
      circle.path.setAttribute('stroke-width', state.width);

      var value = Math.round(circle.value() * 100);
      if (value === 0) {
        circle.setText('');
      } else {
        circle.setText(value + '%');
      }
    }
  });
  bar.text.style.fontFamily = '"Montserrat", sans-serif';
  bar.text.style.fontSize = '1.7rem';
  bar.trail.setAttribute('stroke-linecap', 'round');
  bar.path.setAttribute('stroke-linecap', 'round');

  //Animate the circle when scrolled into view
  window.onscroll = function() {
    if (isScrolledIntoView(jQuery('#' + divid))) bar.animate(countvalue);
    else bar.animate(0); // or bar.set(0)
  }
}
#total-score-circle,
#general-score-circle,
#speed-score-circle,
#privacy-score-circle {
  margin: 0.8em auto;
  width: 100px;
  height: 100px;
  position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/progressbar.js/1.0.1/progressbar.min.js"></script>

<div id="total-score-circle"></div>
<div id="general-score-circle"></div>
<div id="speed-score-circle"></div>
<div id="privacy-score-circle"></div>

在研究这个问题时,我了解到JavaScript只会输出一个循环的最后一个值,我认为这可能是我问题的原因。

所以我尝试用these solutions ...

替换for循环

解决方案1:与以前相同的问题,只有最后一个循环在滚动时动画。

  for (var i in divsValues) {
    (function(){
      var ii = i;
        if (divsValues.hasOwnProperty(ii)) {
          bgCircles(ii, divsValues[ii]);
        }
    })();        
  }

解决方案2:再次出现与以前相同的问题,只有最后一个循环在滚动时动画化。

  for (var i in divsValues) {
    let ii = i;
      if (divsValues.hasOwnProperty(ii)) {
        bgCircles(ii, divsValues[ii]);
      }
  }

解决方案3:再次出现与以前相同的问题,只有最后一个循环在滚动时动画化。

  for (var i in divsValues) {
    try{throw i}
    catch(ii) {
      if (divsValues.hasOwnProperty(ii)) {
        bgCircles(ii, divsValues[ii]);
      }
    }
  }

所以现在我想也许问题不是循环,而是我无法看到或弄明白的东西。

3 个答案:

答案 0 :(得分:2)

你所拥有的循环运行得如此之快以至于浏览器引擎无法呈现更改,我建议您使用setInterval()方法或连续setTimeout()方法,这会给您带来一些延迟代码,以便浏览器可以呈现您正在进行的更改。

对于您的特殊情况,我建议:

var i = 0;
var tobecleared = setInterval(timer,1000);

function timer(){
    var p = get_ith_key_from_divsvalues(i);//implement this method
    console.log(p);
    bgCircles(p, divsValues[p]);
    i++;
    if(i == Object.keys(divsValues).length)
         clearInterval(tobecleared);
}
function get_ith_key_from_divsvalues(i){
     var j = -1;
     for(var property in divsValues){
          j++;
          if(j==i)
                return property;
     }
}

注意: window.onscroll在每次通话中被覆盖,这就是为什么只有最后一个圈子响应。

答案 1 :(得分:2)

你很亲密。

以下是修复程序:


function bgCircles(...)中,使用var在该function's scope中声明bar

var bar = new ProgressBar.Circle(document.getElementById(divid), {

设置滚动到视图中的动画检查器事件后,便会为window.onscroll反复分配新功能。由于您使用的是jQuery,请考虑使用jQuery's .scroll事件处理程序,并像这样使用它:

$(window).scroll(function () {
    if (isScrolledIntoView(jQuery('#' + divid))) bar.animate(countvalue);
    else bar.animate(0); // or bar.set(0)
});

整体解决方案:

//Loop through my divs and create animated circle for each one
function makeCircles() {
  var divsValues = {
    'total-score-circle': 0.75,
    'general-score-circle': 0.80,
    'speed-score-circle': 0.85,
    'privacy-score-circle': 0.90,
  };

  for (var i in divsValues) {
    if (divsValues.hasOwnProperty(i)) {
      bgCircles(i, divsValues[i]);
    }
  }
}
makeCircles();

// Check if element is scrolled into view
function isScrolledIntoView(elem) {
  var docViewTop = jQuery(window).scrollTop();
  var docViewBottom = docViewTop + jQuery(window).height();
  var elemTop = jQuery(elem).offset().top;
  var elemBottom = elemTop + jQuery(elem).height();

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}

//Circle design and animation
function bgCircles(divid, countvalue) {
  // Design the circle using progressbar.js
  var bar = new ProgressBar.Circle(document.getElementById(divid), {
    color: '#ddd',
    // This has to be the same size as the maximum width to
    // prevent clipping
    strokeWidth: 4,
    trailWidth: 4,
    easing: 'easeInOut',
    duration: 1400,
    text: {
      autoStyleContainer: false
    },
    from: {
      color: '#ddd',
      width: 4
    },
    to: {
      color: '#888',
      width: 4
    },
    // Set default step function for all animate calls
    step: function(state, circle) {
      circle.path.setAttribute('stroke', state.color);
      circle.path.setAttribute('stroke-width', state.width);

      var value = Math.round(circle.value() * 100);
      if (value === 0) {
        circle.setText('');
      } else {
        circle.setText(value + '%');
      }
    }
  });
  bar.text.style.fontFamily = '"Montserrat", sans-serif';
  bar.text.style.fontSize = '1.7rem';
  bar.trail.setAttribute('stroke-linecap', 'round');
  bar.path.setAttribute('stroke-linecap', 'round');

  //Animate the circle when scrolled into view
  $(window).scroll(function () {
    if (isScrolledIntoView(jQuery('#' + divid))) bar.animate(countvalue);
    else bar.animate(0); // or bar.set(0)
  });
}
#total-score-circle,
#general-score-circle,
#speed-score-circle,
#privacy-score-circle {
  margin: 0.8em auto;
  width: 100px;
  height: 100px;
  position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/progressbar.js/1.0.1/progressbar.min.js"></script>

<div id="total-score-circle"></div>
<div id="general-score-circle"></div>
<div id="speed-score-circle"></div>
<div id="privacy-score-circle"></div>


注意:

由于我没有编辑任何圆形动画/圆形可见性检查功能,因此我假定您打算滚动查看时的动画的当前状态 功能,就是现在。在当前状态下,您的脚本有/具有以下副作用

  • 如果您根本不滚动页面,那么即使可见,您的圈子也不会开始动画。 解决方案:在创建圆时将可见性检查器线封装到单独的函数中并运行。

  • 如果您滚动一个圆圈,则其带有百分比的动画将进入其默认状态,即0%。 解决方案:更改可见性检查器功能,当由于过度滚动而导致特定元素不可见时,也将该状态返回为可见。这样,即使您在圈子上滚动,您的圈子也将保持在100%。


关于性能和最佳做法:

  • 使用jQuery时,请确保尽可能少地调用jQuery(...)或其简写$(...)使用变量存储元素,属性和数据。

  • 最好将较长/较大的整体式功能分成较小的功能,使其功能范围更窄,但范围也更明确。

  • 使用事件监听器时,请确保运行尽可能少的事件监听器。结构化HTML和JavaScript代码,以清晰有效的方式访问和修改关键元素,属性和数据

答案 2 :(得分:0)

您需要应用两个修复程序。

  1. 当前bar是全局变量,因此它始终是相同的,要修复它,请使用var声明它。

  2. 使用window.addEventListener通过将window.onscroll设置为处理程序,将滚动事件附加到窗口。您会不断覆盖事件处理程序,而addEventListener的使用将允许您附加多个事件处理程序。

//Loop through my divs and create animated circle for each one
$( document ).ready(function(){
function makeCircles() {
  var divsValues = {
    'total-score-circle': 0.75,
    'general-score-circle': 0.80,
    'speed-score-circle': 0.85,
    'privacy-score-circle': 0.90,
  };

  for (var i in divsValues) {
    if (divsValues.hasOwnProperty(i)) {
      bgCircles(i, divsValues[i]);
    }
  }
}
makeCircles();

// Check if element is scrolled into view
function isScrolledIntoView(elem) {
  var docViewTop = jQuery(window).scrollTop();
  var docViewBottom = docViewTop + jQuery(window).height();
  var elemTop = jQuery(elem).offset().top;
  var elemBottom = elemTop + jQuery(elem).height();

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}

//Circle design and animation
function bgCircles(divid, countvalue) {
  // Design the circle using progressbar.js
  var bar = new ProgressBar.Circle(document.getElementById(divid), {
    color: '#ddd',
    // This has to be the same size as the maximum width to
    // prevent clipping
    strokeWidth: 4,
    trailWidth: 4,
    easing: 'easeInOut',
    duration: 1400,
    text: {
      autoStyleContainer: false
    },
    from: {
      color: '#ddd',
      width: 4
    },
    to: {
      color: '#888',
      width: 4
    },
    // Set default step function for all animate calls
    step: function(state, circle) {
      circle.path.setAttribute('stroke', state.color);
      circle.path.setAttribute('stroke-width', state.width);

      var value = Math.round(circle.value() * 100);
      if (value === 0) {
        circle.setText('');
      } else {
        circle.setText(value + '%');
      }
    }
  });
  bar.text.style.fontFamily = '"Montserrat", sans-serif';
  bar.text.style.fontSize = '1.7rem';
  bar.trail.setAttribute('stroke-linecap', 'round');
  bar.path.setAttribute('stroke-linecap', 'round');

  //Animate the circle when scrolled into view
  window.addEventListener('scroll', function() {
    if (isScrolledIntoView(jQuery('#' + divid))) bar.animate(countvalue);
    else bar.animate(0); // or bar.set(0)
  })
}
})
#total-score-circle,
#general-score-circle,
#speed-score-circle,
#privacy-score-circle {
  margin: 0.8em auto;
  width: 100px;
  height: 100px;
  position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/progressbar.js/1.0.1/progressbar.min.js"></script>

<div id="total-score-circle"></div>
<div id="general-score-circle"></div>
<div id="speed-score-circle"></div>
<div id="privacy-score-circle"></div>