You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
6.2 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="tm-flop vertical-align-middle d-inline-block ">
<slot name="default" :value="displayValue">{{ displayValue }}</slot>
</view>
</template>
<script>
/**
* 数字翻牌
* @property {Number} startVal = [] 0,起始值
* @property {Number} endVal = [] 0,最终值
* @property {Number} duration = [] 3000,从起始值到结束值数字变动的时间
* @property {Boolean} autoplay = [] true,是否自动播放
* @property {Number} decimals = [] 0,保留的小数位数
* @property {String} decimal = [] '.',小数点分割符号
* @property {String} separator = [] ',',上了三位数分割的符号
* @property {String} prefix = [] '',前缀
* @property {String} suffix = [] '',后缀
* @property {Boolean} isFrequent = [] false,是否隔一段时间数字跳动,这里的跳动是隔一段时间设置初始值
* @property {Number} frequentTime = [] 5000,跳动间隔时间
* 此库移植自https://github.com/sitonlotus/vue-digital-flop
*/
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame';
export default {
name: 'tm-flop',
components: {
tmTranslate
},
props: {
/**
* @description 起始值
*/
startVal: {
type: Number,
required: false,
default: 0
},
/**
* @description 最终值
*/
endVal: {
type: Number,
required: false,
default: 2021
},
/**
* @description 从起始值到结束值数字变动的时间
*/
duration: {
type: Number,
required: false,
default: 3000
},
/**
* @description 是否自动播放
*/
autoplay: {
type: Boolean,
required: false,
default: true
},
/**
* @description 保留的小数位数
*/
decimals: {
type: Number,
required: false,
default: 0,
validator(value) {
return value >= 0;
}
},
decimal: {
type: String,
required: false,
default: '.'
},
/**
* @description 三位三位的隔开效果
*/
separator: {
type: String,
required: false,
default: ','
},
/**
* @description 前缀
* @example '¥' 人民币前缀
*/
prefix: {
type: String,
required: false,
default: ''
},
/**
* @description 后缀
* @example
*/
suffix: {
type: String,
required: false,
default: ''
},
/**
* @description 是否具有连贯性
*/
useEasing: {
type: Boolean,
required: false,
default: true
},
/**
* @description 是否隔一段时间数字跳动,这里的跳动是隔一段时间设置初始值
*/
isFrequent: {
type: Boolean,
required: false,
default: false
},
/**
* @description 跳动间隔时间
*/
frequentTime: {
type: Number,
required: false,
default: 5000
}
},
data() {
return {
localStartVal: this.startVal,
displayValue: this.formatNumber(this.startVal),
printVal: null,
paused: false,
localDuration: this.duration,
startTime: null,
timestamp: null,
remaining: null,
rAF: null,
timer: null
};
},
computed: {
countDown() {
return this.startVal > this.endVal;
}
},
watch: {
startVal() {
if (this.autoplay) {
this.start();
}
},
endVal() {
if (this.autoplay) {
this.start();
}
}
},
mounted() {
if (this.autoplay) {
this.start();
}
if (this.isFrequent && this.frequentTime) {
this.timer = setInterval(() => {
this.start(this.randomNum(0, this.endVal));
}, this.frequentTime);
}
this.$emit('mountedCallback');
},
destroy(){
this.destroyed();
},
methods: {
easingFn(t = 0, b = 0, c = 0, d = 0) {
let p = (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b;
return p;
},
randomNum(a, b) {
return Math.round(Math.random() * (b - a) + a);
},
start(startVal) {
this.localStartVal = startVal || this.startVal;
this.startTime = null;
this.localDuration = this.duration;
this.paused = false;
this.rAF = requestAnimationFrame(this.count);
},
pauseResume() {
if (this.paused) {
this.resume();
this.paused = false;
} else {
this.pause();
this.paused = true;
}
},
pause() {
cancelAnimationFrame(this.rAF);
},
resume() {
this.startTime = null;
this.localDuration = +this.remaining;
this.localStartVal = +this.printVal;
requestAnimationFrame(this.count);
},
reset() {
this.startTime = null;
cancelAnimationFrame(this.rAF);
this.displayValue = this.formatNumber(this.startVal);
},
count(timestamp) {
if (!this.startTime) this.startTime = timestamp;
this.timestamp = timestamp;
const progress = timestamp - this.startTime;
this.remaining = this.localDuration - progress;
if (this.useEasing) {
if (this.countDown) {
this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration) || 0;
} else {
this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration);
}
} else {
if (this.countDown) {
this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration);
} else {
this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration);
}
}
if (this.countDown) {
this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal;
} else {
this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal;
}
this.displayValue = this.formatNumber(this.printVal);
if (progress < this.localDuration) {
this.rAF = requestAnimationFrame(this.count);
} else {
this.$emit('callback');
}
},
isNumber(val) {
return !isNaN(parseFloat(val));
},
formatNumber(num) {
num = num.toFixed(this.decimals);
num += '';
const x = num.split('.');
let x1 = x[0];
const x2 = x.length > 1 ? this.decimal + x[1] : '';
const rgx = /(\d+)(\d{3})/;
if (this.separator && !this.isNumber(this.separator)) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + this.separator + '$2');
}
}
return this.prefix + x1 + x2 + this.suffix;
}
},
destroyed() {
cancelAnimationFrame(this.rAF);
this.timer && clearInterval(this.timer);
}
};
</script>
<style></style>