From 0eb8a2407e4df00bf322e19e2312677ba919e2be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=B1=9F=E8=BE=B0?= Date: Thu, 16 Nov 2023 02:13:10 -0600 Subject: [PATCH] feat(statistic): abstracted common code from the statistic component (#1644) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(statistic): abstracted common code from the statistic component * chore: add polyfill and fix elapsed * chore: fix lint --------- Co-authored-by: wū yāng --- js/statistic/tween.ts | 144 ++++++++++++++++++++++++++++++++++++++++++ js/statistic/utils.ts | 9 +++ 2 files changed, 153 insertions(+) create mode 100644 js/statistic/tween.ts create mode 100644 js/statistic/utils.ts diff --git a/js/statistic/tween.ts b/js/statistic/tween.ts new file mode 100644 index 0000000000..6c037d32a9 --- /dev/null +++ b/js/statistic/tween.ts @@ -0,0 +1,144 @@ +/** + * Inspired by https://github.com/PengJiyuan/b-tween + * requestAnimationFrame https://caniuse.com/requestanimationframe + * TDesign vue 2 need to ensure compatibility with users who are using IE and Vue2, + * it is necessary to use setInterval instead of requestAnimationFrame when the browser version is less than 9 + */ +import { getIEVersion } from '../utils/helper'; + +export interface TweenSettings { + from: Record; + to: Record; + duration?: number; + delay?: number; + onStart?: (keys: Record) => void; + onUpdate?: (keys: Record) => void; + onFinish?: (keys: Record) => void; +} + +const quartOut = (t: number) => 1 - Math.abs((t - 1) ** 4); + +export default class Tween { + private from: Record; + + private to: Record; + + private duration: number; + + private onStart?: (keys: Record) => void; + + private onUpdate: (keys: Record) => void; + + private onFinish?: (keys: Record) => void; + + private startTime: number; + + private started: boolean; + + private finished: boolean; + + private timer: number | NodeJS.Timer | null; + + private keys: Record; + + constructor({ + from, + to, + duration = 200, + onStart, + onUpdate = () => {}, + onFinish, + }: TweenSettings) { + this.from = from; + this.to = to; + this.duration = duration; + this.onStart = onStart; + this.onUpdate = onUpdate; + this.onFinish = onFinish; + this.startTime = Date.now(); + this.started = false; + this.finished = false; + this.timer = null; + this.keys = {}; + Object.entries(from).forEach(([key, value]) => { + if (this.to[key] === undefined) { + this.to[key] = value; + } + }); + + Object.entries(to).forEach(([key, value]) => { + if (this.from[key] === undefined) { + this.from[key] = value; + } + }); + } + + private time = 0; + + private elapsed = 0; + + private update() { + this.time = Date.now(); + if (this.time < this.startTime || this.finished) return; + + if (this.elapsed >= this.duration) { + this.finished = true; + this.onFinish?.(this.keys); + return; + } + const elapsed = Math.min(this.time - this.startTime, this.duration); + this.elapsed = elapsed; + const progress = quartOut(elapsed / this.duration); + + Object.keys(this.to).forEach((key) => { + const delta = this.to[key] - this.from[key]; + this.keys[key] = this.from[key] + delta * progress; + }); + + if (!this.started) { + this.onStart?.(this.keys); + this.started = true; + } + + this.onUpdate(this.keys); + } + + private polyfillStart() { + const elapsed = Date.now() - this.startTime; + const interval = quartOut(elapsed / this.duration); + + this.timer = setInterval(() => { + this.update(); + if (this.finished) { + clearInterval(this.timer as number); + } + }, interval); + } + + private normalStart() { + const tick = () => { + this.update(); + this.timer = requestAnimationFrame(tick); + + if (this.finished) { + cancelAnimationFrame(this.timer); + this.timer = null; + } + }; + tick(); + } + + public start() { + this.startTime = Date.now(); + // IE < 10 + if (getIEVersion() > 10) this.polyfillStart(); + else this.normalStart(); + } + + public stop() { + // IE < 10 + if (getIEVersion() > 10) clearInterval(this.timer as number); + else cancelAnimationFrame(this.timer as number); + this.timer = null; + } +} diff --git a/js/statistic/utils.ts b/js/statistic/utils.ts new file mode 100644 index 0000000000..f1c869eb6d --- /dev/null +++ b/js/statistic/utils.ts @@ -0,0 +1,9 @@ +/** + * 颜色映射 + */ +export const COLOR_MAP = { + blue: 'var(--td-brand-color)', + red: 'var(--td-error-color)', + orange: 'var(--td-warning-color)', + green: 'var(--td-success-color)', +};