laya 滚动的数字label

岳风畔
2023-12-01

把之前在cocoscreator里面滚动字,在laya中再实现下。

使用方法:

this.label.text = 0; //laya label 控件
let scrollLabel = this.label.addComponent(ScrollLabelComponent);
scrollLabel.setValue(100);

ScrollLabelComonent.ts 代码

​//滚动label 控件
/**
 * 引用github仓库 
 * https://github.com/inorganik/countUp.js/edit/master/dist/countUp.js
 * version = '2.0.7'
 */

/*
// Object 现在存在 __assign方法 若不存添加这个方法 
let __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
*/

/*
    默认参数
*/
let defaultOptions = {
    startVal: 0, // 开始值;
    duration: 2, // 滚动持续时间;
    useGrouping: true, // 千分位效果,例:1000->1,000。默认true
    separator: ',', // 使用千分位时分割符号
    decimal: '.', // 小数位分割符号
    decimalPlaces: 2, // 小数后几位
    prefix: '', // 前置符号
    suffix: '', // 后置符号,可汉字
    smartEasingThreshold: 999,
    smartEasingAmount: 333,
    useEasing: true, // 过渡动画效果,默认ture
    // numerals: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],  //可定制;
    //easingFn,         // easing函数 可定制
    //formattingFn,     // 序列话数值函数 可定制
};
let easingOutQuintic = function (t, b, c, d) {
    var ts = (t /= d) * t;
    var tc = ts * t;
    return b + c * (tc * ts + -5 * ts * ts + 10 * tc + -10 * ts + 5 * t);
}
let easingOutCubic = function (t, b, c, d) {
    var ts = (t /= d) * t;
    var tc = ts * t;
    return b + c * (tc + -3 * ts + 3 * t);
}
let easingOutExpo = function (t, b, c, d) {
    return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
};
let easingInExpo = function (t, b, c, d) {
    return c * (Math.pow(2, 10 * (t / d - 1))) * 1024 / 1023 + b;
};

export default class LabelComponent extends Laya.Script {
    public options;
    public endValue;
    public startValue;
    public finalEndValue;
    public useEasing;
    public countDown;
    public error;
    public paused;
    public startTime;
    public formattingFn;
    public easingFn;
    public frameValue;
    public rAF;
    public duration;
    public callback;
    public remaining;

    constructor() {
        super();
    }
    onAwake() {
        this.options = defaultOptions;
        this.endValue = 0;
        this.startValue = 0;
        this.finalEndValue = null;
        this.useEasing = true;
        this.countDown = false;
        this.error = '';
        this.paused = true;
        this.init();
    }
    onEnable(): void {

    }
    onDisable(): void {

    }
    onDestroy(): void {

    }
    onUpdate(): void {

    }

    public init(): void {
        this.formattingFn = (typeof this.options.formattingFn == 'function') ? this.options.formattingFn : this.formatNumber;
        this.easingFn = (typeof this.options.easingFn == 'function') ? this.options.easingFn : easingInExpo;
        this.startValue = this.validateValue(this.options.startVal);
        this.frameValue = this.startValue;
        this.options.decimalPlaces = Math.max(0 || this.options.decimalPlaces);
        this.options.separator = String(this.options.separator);
        this.useEasing = this.options.useEasing;
        if (this.options.separator === '') {
            this.options.useGrouping = false;
        }
        this.resetDuration();
        this.printValue(this.startValue);
    }
    /**
     * 设置选项
     * @param {obj} options 
     */
    public setOptions(options) {
        this.options = Object.assign(Object.assign({}, defaultOptions), options);
    }
    /**
     * 设置结束数值
     * @param {number} value
     * @param {function} callback 
     */
    public setValue(value, callback) {
        if (this.options) {
            this.options.startVal = Number((this.owner as Laya.Label).text.replace(/,/g, '')) || 0;
        }
        this.endValue = value;
        this.init();
        this.startScroll(callback);
    }
    /**
     * 重置开始数值
     * @param {number} value 
     */
    public resetValue(value = 0) {
        cancelAnimationFrame(this.rAF);
        this.paused = true;
        this.resetDuration();
        this.options.startVal = value;
        this.startValue = this.validateValue(value);
        this.frameValue = this.startValue;
        this.printValue(this.startValue);
    }
    /**
     * 决定方向和easing函数
     */
    public determineDirectionAndSmartEasing() {
        let value = (this.finalEndValue) ? this.finalEndValue : this.endValue;
        this.countDown = (this.startValue > value);
        let animateAmount = value - this.startValue;
        if (Math.abs(animateAmount) > this.options.smartEasingThreshold) {
            this.finalEndValue = value;
            let up = (this.countDown) ? 1 : -1;
            this.endValue = value + (up * this.options.smartEasingAmount);
            this.duration = this.duration / 2;
        } else {
            this.endValue = value;
            this.finalEndValue = null;
        }
        if (this.finalEndValue) {
            this.useEasing = false;
        } else {
            this.useEasing = this.options.useEasing;
        }
    }
    public startScroll(callback) {
        if (this.error) {
            return;
        }
        this.callback = callback;
        if (this.duration > 0) {
            this.determineDirectionAndSmartEasing();
            this.paused = false;
            this.rAF = requestAnimationFrame(this.count.bind(this));
        } else {
            this.printValue(this.endValue);
        }
    }
    /**
     * 暂停恢复
     */
    public pauseResume() {
        if (!this.paused) {
            cancelAnimationFrame(this.rAF);
        } else {
            this.startTime = null;
            this.duration = this.remaining;
            this.startValue = this.frameValue;
            this.determineDirectionAndSmartEasing();
            this.rAF = requestAnimationFrame(this.count.bind(this));
        }
        this.paused = !this.paused;
    }
    /**
     * 恢复
     */
    public pause() {
        cancelAnimationFrame(this.rAF);
        this.paused = true;
    }
    /**
     * 恢复
     */
    public resume() {
        this.startTime = null;
        this.duration = this.remaining;
        this.startValue = this.frameValue;
        this.determineDirectionAndSmartEasing();
        this.rAF = requestAnimationFrame(this.count.bind(this));
        this.paused = false;
    }

    /**
     * 更新到最后的数值
     * @param {number} newEndVal 
     */
    public updateFinalEndValue(newEndVal) {
        cancelAnimationFrame(this.rAF);
        this.startTime = null;
        this.endValue = this.validateValue(newEndVal);
        if (this.endValue === this.frameValue) {
            return;
        }
        this.startValue = this.frameValue;
        if (!this.finalEndValue) {
            this.resetDuration();
        }
        this.finalEndValue = null;
        this.determineDirectionAndSmartEasing();
        this.rAF = requestAnimationFrame(this.count.bind(this));
    }

    /**
     * 每帧去计算
     * @param {number} timestamp 
     */
    public count(timestamp) {
        if (!this.startTime) {
            this.startTime = timestamp;
        }
        let progress = timestamp - this.startTime;
        this.remaining = this.duration - progress;
        if (this.useEasing) {
            if (this.countDown) {
                this.frameValue = this.startValue - this.easingFn(progress, 0, this.startValue - this.endValue, this.duration);
            } else {
                this.frameValue = this.easingFn(progress, this.startValue, this.endValue - this.startValue, this.duration);
            }
        } else {
            if (this.countDown) {
                this.frameValue = this.startValue - ((this.startValue - this.endValue) * (progress / this.duration));
            } else {
                this.frameValue = this.startValue + (this.endValue - this.startValue) * (progress / this.duration);
            }
        }
        // don't go past endValue since progress can exceed duration in the last frame
        if (this.countDown) {
            this.frameValue = (this.frameValue < this.endValue) ? this.endValue : this.frameValue;
        } else {
            this.frameValue = (this.frameValue > this.endValue) ? this.endValue : this.frameValue;
        }
        // decimal
        this.frameValue = Number(this.frameValue.toFixed(this.options.decimalPlaces));
        // format and print value
        this.printValue(this.frameValue);
        // whether to continue
        if (progress < this.duration) {
            this.rAF = requestAnimationFrame(this.count.bind(this));
        } else if (this.finalEndValue !== null) {
            // smart easing
            this.updateFinalEndValue(this.finalEndValue);
        } else {
            if (this.callback) {
                this.callback();
            }
        }
    }

    /**
     * 千分位转换函数
     * @param {number} num 
     */
    public formatNumber(num) {
        let neg = (num < 0) ? '-' : '';
        let result, x, x1, x2, x3;
        result = Math.abs(num).toFixed(this.options.decimalPlaces);
        if (result.indexOf(".") > 0) {
            result = result.replace(/0+?$/, ''); //去掉后面无用的零
            result = result.replace(/[.]$/, ''); //如小数点后面全是零则去掉小数点
        }
        result += '';
        x = result.split('.');
        x1 = x[0];
        x2 = x.length > 1 ? this.options.decimal + x[1] : '';
        if (this.options.useGrouping) {
            x3 = '';
            for (let i = 0, len = x1.length; i < len; ++i) {
                if (i !== 0 && (i % 3) === 0) {
                    x3 = this.options.separator + x3;
                }
                x3 = x1[len - i - 1] + x3;
            }
            x1 = x3;
        }
        // optional numeral substitution
        if (this.options.numerals && this.options.numerals.length) {
            x1 = x1.replace(/[0-9]/g, function (w) {
                return this.options.numerals[+w];
            });
            x2 = x2.replace(/[0-9]/g, function (w) {
                return this.options.numerals[+w];
            });
        }
        return neg + this.options.prefix + x1 + x2 + this.options.suffix;
    }

    /**
     * 千分位转换
     * @param {number} value 
     * @param {number} decimal 
     * @param {boolean} notDeleteZore 
     */
    public formatNumber2(value, decimal = 2, notDeleteZore = false) {
        // value = 990009990900000;
        let ret = (value / 100).toFixed(decimal).toString();
        ret = '' + decimal ? ret.replace(/(\d)(?=(\d{3})+\.)/g, '$1,') : ret.replace(/(\d)(?=(\d{3})+$)/g, '$1,');
        if (!notDeleteZore) {
            if (ret.indexOf(".") > 0) {
                ret = ret.replace(/0+?$/, ''); //去掉后面无用的零
                ret = ret.replace(/[.]$/, ''); //如小数点后面全是零则去掉小数点
            }
        }
        return ret;
    }

    /**
     * 赋值输出
     * @param {number} value 
     */
    public printValue(value) {
        var result = this.formattingFn(value);
        (this.owner as Laya.Label).text = "" + result;
        console.log('[' + this.owner.name + ']' + " print value: " + value + ' format number: ' + result);
    }

    /**
     * 确认是number类型
     * @param {number} value 
     */
    public ensureNumber(value) {
        return (typeof value === 'number' && !isNaN(value));
    }

    /**
     * 验证数值
     * @param {number} value 
     */
    public validateValue(value) {
        var newValue = Number(value);
        if (!this.ensureNumber(newValue)) {
            this.error = "[CountUp] invalid start or end value: " + value;
            return null;
        } else {
            return newValue;
        }
    }

    /**
     * 重置时间
     */
    public resetDuration() {
        this.startTime = null;
        this.duration = Number(this.options.duration) * 1000;
        this.remaining = this.duration;
    }

}

 类似资料: