循环播放动画时,只运行最后一个循环 [英] When looping through animations, only the last loop runs

查看:64
本文介绍了循环播放动画时,只运行最后一个循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是上一个问题的跟进。

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

I have a progressbar.js circle that animates on scroll. If there is just one circle it works as expected.

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

Now I want to create many of these animated circles by looping through an object with different key-values pairs.

例如:

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

对于每个键值对,键是一个div ID,值是告诉动画还有多远。

For each key-value pair, the key is a div ID and the value is number that tells the animation how far to go.

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

Below is the code where I try to implement my loop, but the problem is that only the last circle is animated on scroll. All the circles appear in their "pre-animation" state, but only the last circle actually becomes animated when you scroll to the bottom.

我需要每个圆圈一次动画它位于视口中。

I need each circle to animate once it is in the 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只会输出一个循环的最后一个值,我认为这可能是我问题的原因。

While researching this problem I learned that JavaScript will only output the last value of a loop, which I thought could be the cause of my problem.

所以我试着用这些解决方案 ......

So I tried to replace the for loop with these solutions...

解决方案1:与以前相同的问题,只有最后一个循环在s上设置动画croll。

Solution 1: Same problem as before, only the last loop animates on scroll.

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

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

Solution 2: Again, same problem as before, only the last loop animates on scroll.

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

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

Solution 3: Again, same problem as before, only the last loop animates on scroll.

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

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

So now I'm thinking maybe the problem is not the loop, but something I can't see or figure out.

推荐答案

你们非常接近。



以下是修正:

You were quite close.

Here are the fixes:

function bgCircles(...),使用 var 来声明功能范围中:

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






当您将动画滚动到视图检查器事件时,您将一个新功能分配给 window.onscroll 。由于您使用的是jQuery,请考虑 jQuery的.scroll 事件处理程序并像这样使用它:


When you set the animate scrolled into view checker event, you assign a new function over-and-over to window.onscroll. Since you are using jQuery, consider jQuery's .scroll event handler and use it like this:

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






整体解决方案:




The solution in whole:

//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>

由于我没有编辑您的任何圆圈动画/圈子可见性检查功能,我认为您打算动画的当前状态什么时候滚动视图功能,现在就是这样。在当前状态下,您的脚本会/具有以下副作用

Since I didn't edit any of your circle animation/circle visibility checking functions, I assume you intended the current state of your animate-when-scrolled-and-in-view functionality the way, that it is right now. In this current state, your script does/has the following side-effects:


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

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

If you scroll through a circle, its animation with the percent will going to go to its default state, which is 0%. Solution: change the visibility checker function, when the particular element is not visible because of overscroll, return that state as visible too. This way your circles will stay at 100%, even when you scroll over them.


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

  • When using jQuery, make sure to call jQuery(...) or its shorthand $(...) as few times as possible. Use variables to store elements, properties, and data.

最好将较长/较大的单片函数分隔为功能较小的功能,功能范围更窄但也更清晰。

It's better to separate longer/larger, monolithic functions into smaller functions with a more narrow, but also more clear scope of functionality.

使用事件监听器时,请务必尽量少运行尽可能。构建HTML和JavaScript代码,以清晰且高效的方式访问和修改关键元素,属性和数据

When using event listeners, make sure to run as few of them as possible. Structure your HTML and your JavaScript code to have clear and performant ways to access and modify your crucial elements, properties, and data.

这篇关于循环播放动画时,只运行最后一个循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆