uni-item-manage/tm-vuetify/components/tm-flop/tm-flop.vue
2023-10-18 15:52:41 +08:00

274 lines
6.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>