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

这是我上一个问题的后续内容。

我有一个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 = 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; } 
   


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

所以我尝试用这些解决方案替换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]); } } } 

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

你很亲密。

以下是修复:


function bgCircles(...) ,使用var来声明该函数范围内的bar

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

将动画滚动到视图检查器事件时,可以将一个新function分配给window.onscroll 。 由于您使用的是jQuery,请考虑使用jQuery 的.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; } 
   

您拥有的循环将运行得如此之快以至于浏览器引擎无法呈现更改,我建议您使用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. 目前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; }