javascript - 循环动画时,只有最后一个循环运行

标签 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,值是告诉动画要走多远的数字。

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

我需要每个圆圈在视口(viewport)中进行动画处理。

//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]);
      }
    }
  }

所以现在我想问题可能不是循环,而是我看不到或想不通的东西。

最佳答案

你们非常接近。

修复方法如下:


函数 bgCircles(...) 中,使用 var 声明 function's scope 中的 bar :

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

当您设置动画滚动到 View 检查器事件时,您将一个新函数反复分配给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>


注意事项:

因为我没有编辑你的任何圆圈动画/圆圈可见性检查功能,我假设你想要你的animate-when-scrolled-and-in-view 的当前状态 功能的方式,它是现在。在此当前状态下,您的脚本会/有以下副作用:

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

  • 如果您滚动圆圈,它的百分比动画将转到默认状态,即 0%。 解决方案:更改可见性检查器功能,当特定元素因过度滚动而不可见时,也将该状态返回为可见。这样,您的圈子将保持在 100%,即使您在它们上滚动也是如此。


关于性能和最佳实践:

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

  • 最好将更长/更大的整体功能分成更小的功能,功能范围更窄但也更清晰。

  • 使用事件监听器时,请确保尽可能少地运行它们。构建您的 HTML 和 JavaScript 代码,以清晰且高效的方式来访问和修改您的关键元素、属性和数据

关于javascript - 循环动画时,只有最后一个循环运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50893984/

相关文章:

javascript - 在javascript中如何更改以下日期格式

JavaScript ID 标记选择

javascript - 将 noConflict() 与 jquery 库一起使用

javascript - 如何创建 knockout 可观察数组/对象的深拷贝

php - while循环mysql/PHP超过执行时间

javascript - 尝试替换 $location.search 时如何避免转义?

php - 如何在没有数据库的情况下显示当前在线用户?或者至少非常有效。最好用 PHP

javascript - 如何在ReactJS中迭代数据并在JSX中显示它们?

excel - 使用 For 循环 VBA 使用另一个工作表中的值填充单元格

javascript - 如何通过 Angular 中的ng-style范围添加部分css属性值