Skip to content

Commit 1af67d5

Browse files
committed
fix: capture update starts without context
setAttributesAndPreventAnimate may run before consumers attach context.diffAttrs. It still overwrites transient attributes with final update values. Capture the pre-handoff frame from submitted attrs when no context diff exists. Update can later consume the same key/value target instead of reading final attribute. Constraint: VChart update can call prevent-animate before attaching diffAttrs Rejected: Require consumers to attach diffAttrs first | leaks VRender ordering Rejected: Read starts from baseAttributes | breaks interrupted transient handoff Confidence: high Scope-risk: narrow Directive: Keep update start snapshots Graphic-owned and transient-only Tested: vrender-animate targeted regression red then green Tested: vrender-animate rushx test Tested: vrender-core state animation and transition targeted tests Tested: rush compile for vrender-core and vrender-animate Tested: vrender-core eslint; animation perf smoke; git diff --check Not-tested: VChart browser consumer smoke
1 parent c6ffbe8 commit 1af67d5

2 files changed

Lines changed: 57 additions & 3 deletions

File tree

packages/vrender-animate/__tests__/unit/animation-runtime-attribute.test.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
import { application, createGroup, createLine, createRect, DefaultGraphicService } from '@visactor/vrender-core';
1+
import {
2+
application,
3+
AttributeUpdateType,
4+
createGroup,
5+
createLine,
6+
createRect,
7+
DefaultGraphicService
8+
} from '@visactor/vrender-core';
29
import { registerAnimate } from '../../src/register';
310
import { registerCustomAnimate } from '../../src/custom/register';
411
import { AnimateExecutor } from '../../src/executor/animate-executor';
512
import { TagPointsUpdate } from '../../src/custom/tag-points';
13+
import { Update } from '../../src/custom/update';
614
import { DefaultTimeline } from '../../src/timeline';
715
import { ManualTicker } from '../../src/ticker/manual-ticker';
816

@@ -1070,6 +1078,53 @@ describe('D3 pre-handoff animation runtime', () => {
10701078
expect(rect.getFinalAttribute().height).toBe(next.height);
10711079
});
10721080

1081+
test('update animation captures start attrs when diff context is attached after prevent-animate handoff', () => {
1082+
ensureAnimationRuntime();
1083+
1084+
const oldY = 46.90909090909094;
1085+
const finalY = 3;
1086+
const rect = createRect({
1087+
x: 0,
1088+
y: oldY,
1089+
width: 10,
1090+
height: 34
1091+
});
1092+
rect.setFinalAttributes({ ...rect.attribute });
1093+
1094+
const diffAttrs = {
1095+
x: 974.1100285714286,
1096+
y: finalY,
1097+
height: 34
1098+
};
1099+
1100+
rect.setAttributesAndPreventAnimate(diffAttrs, false, {
1101+
type: AttributeUpdateType.DEFAULT
1102+
});
1103+
1104+
expect(rect.attribute.y).toBe(finalY);
1105+
expect((rect as any).baseAttributes.y).toBeCloseTo(oldY);
1106+
1107+
(rect as any).context = {
1108+
diffAttrs,
1109+
finalAttrs: diffAttrs
1110+
};
1111+
1112+
const animate = rect.animate().play(new Update(null, null, 300, 'linear', {} as any));
1113+
animate.advance(1);
1114+
1115+
const step = (animate as any)._firstStep;
1116+
expect(step.getFromProps().y).toBeCloseTo(oldY);
1117+
expect(step.getEndProps().y).toBe(finalY);
1118+
expect(rect.attribute.y).toBeGreaterThan(finalY);
1119+
expect(rect.attribute.y).toBeLessThan(oldY);
1120+
1121+
animate.advance(300);
1122+
1123+
expect(rect.attribute.y).toBe(finalY);
1124+
expect((rect as any).baseAttributes.y).toBe(finalY);
1125+
expect(rect.getFinalAttribute().y).toBe(finalY);
1126+
});
1127+
10731128
test('superseded executor update cannot commit stale layout when it ends late', () => {
10741129
const { group, ticker, graphicService } = createStageHarness('executor-update-stale-end');
10751130
const threeItemLayout = {

packages/vrender-core/src/graphic/graphic.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,11 +1329,10 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
13291329
context?: ISetAttributeContext
13301330
) {
13311331
const graphicContext = this.context as Record<string, any> | undefined;
1332-
const diffAttrs = graphicContext?.diffAttrs as Record<string, any> | undefined;
1332+
const diffAttrs = (graphicContext?.diffAttrs as Record<string, any> | undefined) ?? (params as Record<string, any>);
13331333
const updateType = context?.type;
13341334
if (
13351335
!keys.length ||
1336-
!graphicContext ||
13371336
!diffAttrs ||
13381337
updateType === AttributeUpdateType.STATE ||
13391338
(updateType != null &&

0 commit comments

Comments
 (0)