在一些数据展示的专题页里,有时候希望数字能动态从某一个数变化到另一个数,以此来吸引用户眼球,突出数据。

于是有了下文。

在这里,我用了两种方式:一种是原生的JavaScript,另一种是jQuery插件。

数字线性变化的原理很简单,就是让数字增量变化,并循环动画。

原生JS版

首先获取DOM元素。为了兼容到IE6,兼容性方法如下:

var domUtil = {
    // 获取DOM元素
    get: function(query) {
        var _this = this;
        if(document.querySelector) {
            return document.querySelector(query);
        } else {
            var elements = document;
            var queryStrArray = query.split(/ +/);
            for(var i = 0; i < queryStrArray.length; i++) {
                var domName = queryStrArray[i];
                elements = _this.getElementsOfParentNode(domName, elements);
            }
            if(elements.length == 1) {
                return elements[0];
            } else {
                return elements;
            }
        }
    },
    // 获取DOM元素
    getElementsOfParentNode: function(domName, parentNode) {
        var _this = this;
        parentNode = parentNode || document;
        domName = domName.trim();
        var regExps = {
            id: /^#/,
            class: /^/
        };
        if(regExps.id.test(domName)) {
            domName = domName.replace(/^\#/g, "");
            return parentNode.getElementById(domName);
        } else if(regExps.class.test(domName)) {
            domName = domName.replace(/^./g, "");
            return _this.getElementsByClassName(domName, parentNode);
        } else {
            return parentNode.getElementsByTagName(domName);
        }
    },
    // 获取class元素的兼容方法
    getElementsByClassName: function(className, parentNode) {
        if(parentNode.getElementsByClassName){
            return parentNode.getElementsByClassName(className);
        } else {
            className = className.replace(/^ +| +$/g,"");
            var classArray = className.split(/ +/);
            var eles = parentNode.getElementsByTagName("*");
            for(var i = 0;i < classArray.length; i++){
                var classEles = [];
                var reg = new RegExp("(^| )" + classArray[i] + "( |$)");
                for(var j = 0;j < eles.length; j++){
                    var ele = eles[j];
                    if(reg.test(ele.className)){
                        classEles.push(ele);
                    }
                }
                eles = classEles;
            }
            return eles;
        }
    }
};
/*
 * 数字动画(目前仅支持数字动画的线性变化)
 * options参数:
 *     element  {String}    DOM元素query字符串
 *     from   {Number}    起始数字
 *     to   {Number}    终点数字
 *     duration   {Number}   动画时间
 *     callback   {Function}   数字变化时的回调函数
 */
var animatingNumber = function(options) {
    this.element = domUtil.get(options.element);
    this.startNum = options.from;
    this.endNum = options.to;
    this.duration = options.duration || 2000;
    this.callback = options.callback;

    this.timer = null;
};

animatingNumber.prototype = {
    start: function() {
        var _this = this;
        _this.animate();
    },
    stop: function() {
        if(this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
        }
    },
    animate: function() {
        var _this = this;
        var curNum = _this.startNum;
        var animateTime = 0;
        var range = _this.endNum - _this.startNum;
        var timerStep = Math.abs( Math.floor(_this.duration / range) );
        timerStep = timerStep > 20 ? timerStep : 20;
        var numStep = (range / _this.duration) * timerStep;

        _this.stop();

        (function animate() {
            _this.timer = setTimeout(function() {
                curNum = Math.ceil( curNum + numStep );
                if( (_this.endNum > _this.startNum && curNum >= _this.endNum) || (_this.endNum < _this.startNum && curNum <= _this.endNum) ) {
                    curNum = _this.endNum;
                }
                _this.element.innerText = curNum;
                animateTime++;
                if(typeof this.callback == 'function') {
                    this.callback(curNum);
                }
                animate();
                if(curNum >= _this.endNum) {
                    _this.stop();
                }
            }, timerStep);
        })();
    }
};

animatingNumber.create = function(options) {
    return new animatingNumber(options);
};

使用:

<p>Number: <span class='dynamicNum'>500</span></p>

<script>
    animatingNumber.create({
        element: '.dynamicNum',
        from: 1,
        to: 500,
        duration: 2000
    }).start();
</script>

效果如下:

jQuery插件版

原理同上,只是DOM元素获取使用jQuery方法,并把数字动画方法封装成jQuery插件。如下:

/*
 * 数字动画(目前仅支持数字动画的线性变化)
 * options参数:
 *     from   {Number}    起始数字
 *     to   {Number}    终点数字
 *     duration   {Number}   动画时间
 *     callback   {Function}   数字变化时的回调函数
 */
(function( $ ) {
    $.fn.animatingNumber = function(options) {
        var settings = {
            element: this,
            startNum: options.from,
            endNum: options.to,
            duration: options.duration || 2000,
            callback: options.callback
        };
        var timer = null;

        var methods = {
            start: function() {
                var _this = this;
                _this.animate();
            },
            stop: function() {
                if(timer) {
                    clearTimeout(timer);
                    timer = null;
                }
            },
            animate: function() {
                var _this = this;
                var curNum = settings.startNum;
                var animateTime = 0;
                var range = settings.endNum - settings.startNum;
                var timerStep = Math.abs( Math.floor(settings.duration / range) );
                timerStep = timerStep > 20 ? timerStep : 20;
                var numStep = (range / settings.duration) * timerStep;

                _this.stop();

                (function animate() {
                    timer = setTimeout(function() {
                        curNum = Math.ceil( curNum + numStep );
                        if( (settings.endNum > settings.startNum && curNum >= settings.endNum) || (settings.endNum < settings.startNum && curNum <= settings.endNum) ) {
                            curNum = settings.endNum;
                        }
                        settings.element.text(curNum);
                        animateTime++;
                        if(typeof settings.callback == 'function') {
                            settings.callback(curNum);
                        }
                        animate();
                        if(curNum >= settings.endNum) {
                            _this.stop();
                        }
                    }, timerStep);
                })();
            }
        };
        return this.each(function() {
            return methods.start();
        });

    };
})( jQuery );

使用:

<p>Number: <span class='dynamicNum'></span></p>

<script>
$('.dynamicNum').animatingNumber({
    from: 1,
    to: 1000,
    duration: 2000
});
</script>

效果如下:

最后

后期会考虑加上缓动函数的选择项。

本文作者:子匠_Zijor,转载请注明出处:http://www.dengzhr.com/js/1328