fix: StatsSummary 增量更新时只动画变化的列,不再全部重播

首次加载仍全部 count-up。后续 items 变化时对比新旧值,
只对值发生变化的列触发动画,其他列保持静止。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
liulujian 2026-07-05 00:01:28 +08:00
parent 7c1e0a4e18
commit 847f3a52ea

View File

@ -22,15 +22,29 @@ export default {
items: { type: Array, default: () => [] },
},
data() {
return { displayValues: [], _animated: false, _observer: null };
return { displayValues: [], _animated: false, _observer: null, _prevItems: null };
},
watch: {
items: {
immediate: true,
handler() {
this.displayValues = this.items.map((v) => v.value);
handler(cur, old) {
if (!old || !this._prevItems) {
//
this.displayValues = cur.map((v) => v.value);
this._animated = false;
this._prevItems = cur.map((v) => v.value);
this.$nextTick(() => this._observe());
return;
}
//
const prev = this._prevItems;
cur.forEach((item, idx) => {
if (String(item.value) !== String(prev[idx])) {
//
this._animateOne(idx, Number(item.value));
}
});
this._prevItems = cur.map((v) => v.value);
},
},
},
@ -57,6 +71,25 @@ export default {
}, { threshold: 0.2 });
this._observer.observe(root);
},
_animateOne(idx, target) {
if (Number.isNaN(target)) {
this.$set(this.displayValues, idx, target);
return;
}
const startVal = Number(this.displayValues[idx]) || 0;
const diff = target - startVal;
if (diff === 0) return;
const start = performance.now();
const duration = Math.min(600 + Math.abs(diff) * 3, 1200);
const tick = (now) => {
const p = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - p, 3);
const current = Math.round(startVal + eased * diff);
this.$set(this.displayValues, idx, current.toLocaleString());
if (p < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
},
_animateAll() {
if (this._animated) return;
this._animated = true;