fix: StatsSummary 增量更新时只动画变化的列,不再全部重播
首次加载仍全部 count-up。后续 items 变化时对比新旧值, 只对值发生变化的列触发动画,其他列保持静止。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7c1e0a4e18
commit
847f3a52ea
@ -22,15 +22,29 @@ export default {
|
|||||||
items: { type: Array, default: () => [] },
|
items: { type: Array, default: () => [] },
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return { displayValues: [], _animated: false, _observer: null };
|
return { displayValues: [], _animated: false, _observer: null, _prevItems: null };
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
items: {
|
items: {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler() {
|
handler(cur, old) {
|
||||||
this.displayValues = this.items.map((v) => v.value);
|
if (!old || !this._prevItems) {
|
||||||
this._animated = false;
|
// 首次加载:全部初始化
|
||||||
this.$nextTick(() => this._observe());
|
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 });
|
}, { threshold: 0.2 });
|
||||||
this._observer.observe(root);
|
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() {
|
_animateAll() {
|
||||||
if (this._animated) return;
|
if (this._animated) return;
|
||||||
this._animated = true;
|
this._animated = true;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user