数字限时增长效果实现:numberGrow.js------遇到的产品提的要求
来源:互联网 发布:标准化工程专业 知乎 编辑:程序博客网 时间:2024/06/08 08:41
JavaScript实现数字动态变化效果
我在手机上使用挖财记账 APP,左右滑动页面时首屏上的三个数字都会同时做动态滚动,从零渐增到当前数字,整个动画效果大概在 500ms 内完成,三个数字同时结束滚动。尝试用JS实现这个效果。下面是效果演示:
只用几行JS代码就能实现效果。原理也是很简单,触发事件后将数字变成 0,然后以逐步增加到最终显示的数值。这里使用 requestAnimationFrame 方法代替 setTimeout 实现动画。
function countUp(elem, endVal, startVal, duration, decimal) { //传入参数依次为 数字要变化的元素,最终显示数字,数字变化开始值,变化持续时间,小数点位数 var startTime = 0; var dec = Math.pow(10, decimal); var progress,value; function startCount(timestamp) { if(!startTime) startTime = timestamp; progress = timestamp - startTime; value = startVal + (endVal - startVal) * (progress / duration); value = (value > endVal) ? endVal : value; value = Math.floor(value*dec) / dec; elem.innerHTML = value.toFixed(decimal); progress < duration && requestAnimationFrame(startCount) } requestAnimationFrame(startCount)}
这是上周工作中写到的一个功能,大概的效果就是页面中有几处数字,统计公司的一些业务信息,需要在第一次出现的时候,做一个从0开始增长,大概2秒自动增长到真实数值,并停止增长的效果。这个问题的重点在于解决如何保证不同大小的数字都在2秒左右的时间自动增长完成,以及还有考虑延迟初始化的处理。后面这一点是为了保证,只有当数字第一次进入浏览器可视区域的时候,才会看到效果,因为这些数字有可能不在首屏的内容内,必须保证当用户滚动操作将数字显示出来的那一刻才能看到效果。
demo地址:
http://liuyunzhuge.github.io/blog/numerGrow/dist/html/demo.html
代码地址:
https://github.com/liuyunzhuge/blog/tree/master/numerGrow
代码运行说明见git项目内的readme.md。
本文内容有补充修改,详见本文最后的补充说明!
1. 实现思路
先来看看如何保证大小不同的数字都在规定的时间内都能增长完成。
这个问题可以类比到曾经物理课的一些知识,就是速度路程与时间的关系。在这个问题中,最终的数字大小代表路程,单位时间内每个数字增长的值代表速度。由于路程不同,要走的时间相同,所以每个数字的增长速度也就不会相同。要解决这个问题,只要求出速度即可,因为时间和路程都是已知的。
但是在物理里面,速度的基本单位都是以秒或者小时为单位的,比如3m/s,30km/h,在程序里面显然是不能用秒或者小时的,因为这些单位太大了,而且要解决我们的问题,显然要用到计时器,计时器的单位是毫秒,所以在计算速度的时候,要以ms为单位。比如要显示的数值如果是100,规定的时间为2s,也就是2000ms,那么每ms要增加的数值就是100/2000,根据这个设想,可以得出如下的程序实现:
这个实现虽然从理论上是可行的,但是实际运行的时候,会发现这个增长的效果会远远超过规定的时间,原因可能在于setInterval里面的函数执行也是需要耗费时间的,而且不一定能在定时器的间隔内就执行完,所以这些额外执行的时间跟每次执行的间隔累计起来就会超过规定的时间。要解决这个问题,我想到了一篇文章里面提到的关于帧率的问题:
http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html
如果网页动画能够做到每秒60帧,就会跟显示器同步刷新,达到最佳的视觉效果。这意味着,一秒之内进行60次重新渲染,每次重新渲染的时间不能超过16.66毫秒。
我们可以把setInterval的每次执行都看成是一帧,然后把setInterval的执行间隔改成16,只要它的回调函数执行时间不超过16ms,那么这个计时器累计运行的时间就只跟间隔时间有关系,而跟回调函数的执行时间没有关系,因为回调函数是在回调间隔时间内执行完的!这就是解决前面问题的关键:
1)把计时器的间隔改成16ms
2)把速度从每ms增加的数值改成每16ms增加的数值
最终正确的实现如下(对应的代码是https://github.com/liuyunzhuge/blog/blob/master/numerGrow/src/js/mod/numberGrow.js):
基于这个实现去测试,会发现最终的运行结果与规定的时间只有几十ms的差别,基本上已经达到我们的要求了。这几十毫秒的差距,我觉得来自于浏览器对于setInterval的管理,如果想要十分精准地在规定时间内完成这个效果,我还没有想到好的方法,希望有这个思路的朋友愿意分享出来。
事实上,定时器的间隔不用16,用8, 9, 10, 18, 20, 24也都可以,效果跟16差不多,因为定时器的回调函数执行在浏览器正常的情况下肯定不需要8ms,里面啥都没干呢。。。用8, 9, 10, 18, 20, 24还是16的区别在于数字变化的速度看起来不一样而已,间隔越小变化越快,间隔越大变化越慢,所以给人的视觉体验不同。用16是因为它比较接近于16.66ms这个数值。
以上部分是关于如何保证大小不同的数字都在规定的时间内都能增长完成的说明,下面来看看如何做滚动时的懒加载。
我的思路考虑地相对简单,借助滚动事件,监听各个元素是否完全进入浏览器的可视区域,只有当它完全在浏览器可视区域的时候才初始化,并且只执行一次,当某个类型的组件全部都初始化以后,还会做一个destroy的处理,以便提供页面性能。
这部分的实现对应的代码是:https://github.com/liuyunzhuge/blog/blob/master/numerGrow/src/js/mod/scrollLazyInit.js。
其中有几个关键点可以再在博客里说明一下:
1)options
scrollLazyInit提供了两个option,一个ns,表示命名空间,用来注册scroll事件,因为这个组件可能不只有numberGrow才会用到,页面当中其它耗时的组件也可以利用这个组件来做简单的懒初始,有了这个个ns就可以管理不同的组件了;还有一个delay就是滚动回调节流时的间隔,一般不会用到。
在使用scrollLazyInit的时候,必须先实例化才能使用,实例化的时候可以传递ns和delay参数:
2)add方法
每个srollLazy的实例都有两个实例方法,其中一个就是add方法,用来将要延迟初始化的功能添加到scrollLazy来管理:
add方法有两个参数,第一个是要延迟初始化的dom元素,要用它来判断是否完全进入可视区域,第二个是当元素完全进入可视区域时回调,在这个回调里面来做组件初始化,就如上图所示。
3)start方法
每个scrollLazy实例的另外一个实例方法就是start,这个其实就是添加滚动监听而已。在把所有的延迟初始化的组件都add完之后,再调用这个方法即可:
由于它的实现并不复杂,而且也不属于本文重点,原本这一部分功能是在numberGrow里面的,后来考虑到职责分离,才单独写成了另外一个组件,代码只有60行,相信您的能力,肯定能直接看明白源码。
另外还值得一说的是,这个scrollLazy还有优化的地方,就是在判断初始化的时机这一块,因为目前是判断元素完全进入可视区域的时候才初始化,这对于一些高度很小的元素来说,没有问题,但是对于高度可能超过可视区域的元素来说,肯定是不行的,所以在使用的时候要注意这个点。
2. 使用说明
这个功能在使用的时候,可以直接通过data属性来注册,因为这种效果型的功能,基本上都没有业务逻辑,不必要放到跟业务逻辑相关的js里面去,所以只要在html上注册即可:
第一个data-ride=”numberGrow”不能省,因为在numberGrow.js里面,是通过这个属性来找到需要自动注册的元素的。后面的value和time分别表示要增长的真实数值和增长的有效时间。
3. 本文小结
本文介绍了自己关于一个简单的网页效果的实现思路,因为觉得那个类比物理中的速度时间路程的点比较有趣,所以把它分享出来,希望对您有所参考价值,谢谢阅读:)
补充于2016-06-02
本文中提供的实现有瑕疵,虽然在demo中看到的运行结果跟预期一致,但是存在问题,这个问题要感谢 上位者的怜悯在评论中帮我指正出来,并提出了一个更佳的实现方式。这个问题是:本文的实现思路,会受到代码执行时间的影响,这个代码执行时间包括定时器回调函数的执行时间以及页面中其它js代码的执行时间,代码执行时间越长,会导致最终的效果持续时间越偏离规定的运行时间。
虽然从思路上来说,本文的想法很好,类比了物理中速度与时间以及路程的关系,但是这毕竟是代码执行环境,无法保证“匀速”增长。更好的实现方式是,上位者的怜悯在评论区中提出的按照比例来计算当前数值的方法,计算公式是:当前值 = 总数值 * (已经运行的时间 / 总时间)。
我把他的代码重新整理了一下,并封装为了numberGrowBetter.js,放在js/mod文件夹下,源码请查看:
https://github.com/liuyunzhuge/blog/blob/master/numerGrow/src/js/mod/numberGrowBetter.js
这个实现对应的demo:
http://liuyunzhuge.github.io/blog/numerGrow/dist/html/demo2.html
- 数字限时增长效果实现:numberGrow.js------遇到的产品提的要求
- PHP实现假装商品限时抢购繁忙的效果
- 使用ValueAnimator实现数字增长效果
- 关于支付宝数字增长动画的效果
- 统计数字限时增长效果实现:------------简单有效版(配合JQ使用)
- Android效果之手机支付宝中增长的数字效果
- 自定义View实现自动数字增长的TextView
- 满足人工智能日益增长的要求
- Android之增长的数字
- js实现限时抢倒计时
- 产品经理的任职要求
- js实现数字变动效果
- 《js倒计时效果》之限时抢
- 《js倒计时效果》之限时抢
- JS-商场限时抢倒计时效果
- 根据JS实现伪增长的注册人数
- 【转载】js实现产品放大镜展示效果
- 大大的限时特惠
- 阿里开源Mysql分布式中间件:Cobar
- 距离向量算法
- Java机试题求兔子数量
- spring揭秘----(1)
- TCP中的四个计时器
- 数字限时增长效果实现:numberGrow.js------遇到的产品提的要求
- 欢迎使用CSDN-markdown编辑器
- 使用清华tuna下载anaconda安装ipython notebook
- nginx源码学习(二)
- C语言 字符串截取
- 123. Best Time to Buy and Sell Stock III
- hdu 1839 Delay Constrained Maximum Capacity Path 二分+最短路
- 关于java中AWT和Swing之间的区别鱼联系?
- Ansible 系列之 Ad-Hoc介绍及使用