此次分享,主要是计数器动画(例如:0-100 会有一个变化的过程)
第一步:在项目中创建一个JS
class CountUp {
/**
* 参数介绍
* 开始值 结束值 小数点 时长 ?
*/
constructor(startVal, endVal, decimals, duration, options = {}) {
Object.assign(this, {
startVal,
endVal,
decimals,
duration,
options,
})
this.__init()
}
/**
* 初始化
*/
__init() {
this.lastTime = 0
// merge options
this.mergeOptions(this.options)
this.startVal = Number(this.startVal)
this.cacheVal = this.startVal
this.endVal = Number(this.endVal)
this.countDown = (this.startVal > this.endVal)
this.frameVal = this.startVal
this.decimals = Math.max(0, this.decimals || 0)
this.dec = Math.pow(10, this.decimals)
this.duration = Number(this.duration) * 1000 || 2000
// format startVal on initialization
this.printValue(this.formattingFn(this.startVal))
}
/**
* 默认参数
*/
setDefaultOptions() {
return {
useEasing : true, // toggle easing
useGrouping : true, // 1,000,000 vs 1000000
separator : `,`, // character to use as a separator
decimal : `.`, // character to use as a decimal
easingFn: null, // optional custom easing closure function, default is Robert Penner's easeOutExpo
formattingFn: null, // optional custom formatting function, default is this.formatNumber below
printValue(value) {}, // printValue
}
}
/**
* 合并参数
*/
mergeOptions(options) {
const defaultOptions = this.setDefaultOptions()
// extend default options with passed options object
for (let key in defaultOptions) {
if (defaultOptions.hasOwnProperty(key)) {
this.options[key] = typeof options[key] !== `undefined` ? options[key] : defaultOptions[key]
if (typeof this.options[key] === `function`) {
this.options[key] = this.options[key].bind(this)
}
}
}
if (this.options.separator === ``) { this.options.useGrouping = !1 }
if (!this.options.prefix) this.options.prefix = ``
if (!this.options.suffix) this.options.suffix = ``
this.easingFn = this.options.easingFn ? this.options.easingFn : this.easeOutExpo
this.formattingFn = this.options.formattingFn ? this.options.formattingFn : this.formatNumber
this.printValue = this.options.printValue ? this.options.printValue : function() {}
}
/**
* 创建定时器
*/
requestAnimationFrame(callback) {
let currTime = new Date().getTime()
let timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
let timeout = setTimeout(() => {
callback.bind(this)(currTime + timeToCall)
}, timeToCall)
this.lastTime = currTime + timeToCall
return timeout
}
/**
* 清空定时器
*/
cancelAnimationFrame(timeout) {
clearTimeout(timeout)
}
/**
* 格式化数字
*/
formatNumber(nStr) {
nStr = nStr.toFixed(this.decimals)
nStr += ``
let x, x1, x2, rgx
x = nStr.split(`.`)
x1 = x[0]
x2 = x.length > 1 ? this.options.decimal + x[1] : ``
rgx = /(\d+)(\d{3})/
if (this.options.useGrouping) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, `$1` + this.options.separator + `$2`)
}
}
return this.options.prefix + x1 + x2 + this.options.suffix
}
/**
* 过渡效果
*/
easeOutExpo(t, b, c, d) {
return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
}
/**
* 计数函数
*/
count(timestamp) {
if (!this.startTime) { this.startTime = timestamp }
this.timestamp = timestamp
const progress = timestamp - this.startTime
this.remaining = this.duration - progress
// to ease or not to ease
if (this.options.useEasing) {
if (this.countDown) {
this.frameVal = this.startVal - this.easingFn(progress, 0, this.startVal - this.endVal, this.duration)
} else {
this.frameVal = this.easingFn(progress, this.startVal, this.endVal - this.startVal, this.duration)
}
} else {
if (this.countDown) {
this.frameVal = this.startVal - ((this.startVal - this.endVal) * (progress / this.duration))
} else {
this.frameVal = this.startVal + (this.endVal - this.startVal) * (progress / this.duration)
}
}
// don't go past endVal since progress can exceed duration in the last frame
if (this.countDown) {
this.frameVal = (this.frameVal < this.endVal) ? this.endVal : this.frameVal
} else {
this.frameVal = (this.frameVal > this.endVal) ? this.endVal : this.frameVal
}
// decimal
this.frameVal = Math.round(this.frameVal*this.dec)/this.dec
// format and print value
this.printValue(this.formattingFn(this.frameVal))
// whether to continue
if (progress < this.duration) {
this.rAF = this.requestAnimationFrame(this.count)
} else {
if (this.callback) { this.callback() }
}
}
/**
* 启动计数器
*/
start(callback) {
this.callback = callback
this.rAF = this.requestAnimationFrame(this.count)
return !1
}
/**
* 停止计数器
*/
pauseResume() {
if (!this.paused) {
this.paused = !0
this.cancelAnimationFrame(this.rAF)
} else {
this.paused = !1
delete this.startTime
this.duration = this.remaining
this.startVal = this.frameVal
this.requestAnimationFrame(this.count)
}
}
/**
* 重置计数器
*/
reset() {
this.paused = !1
delete this.startTime
this.startVal = this.cacheVal
this.cancelAnimationFrame(this.rAF)
this.printValue(this.formattingFn(this.startVal))
}
/**
* 更新计数器
*/
update(newEndVal) {
this.cancelAnimationFrame(this.rAF)
this.paused = !1
delete this.startTime
this.startVal = this.frameVal
this.endVal = Number(newEndVal)
this.countDown = (this.startVal > this.endVal)
this.rAF = this.requestAnimationFrame(this.count)
}
}
export default CountUp
第二步,在项目中引用和使用
// 引入创建好的类
import CountUp from './countUp.js';
// 使用
// 参数介绍 开始值 结束值 小数点 时长 内置的放阿飞
let countUp = new CountUp(0, resultScore, 0, 3.8, {
printValue(value) {
// 赋值过程,把value赋值给页面要显示的地方
_this.resultScore = value;
}
});
countUp.start();